eth_getCode: add support for GoQuorum private tx (#2380)

* eth_getCode try to get from private state first

Signed-off-by: Sally MacFarlane <sally.macfarlane@consensys.net>

* add container test for eth_getCode

Signed-off-by: Sally MacFarlane <sally.macfarlane@consensys.net>
pull/2283/head
Sally MacFarlane 3 years ago committed by GitHub
parent 2b24f43a8a
commit 33a9541eef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      container-tests/tests/src/test/java/org/hyperledger/besu/tests/container/ContainerTestBase.java
  2. 27
      container-tests/tests/src/test/java/org/hyperledger/besu/tests/container/ContainerTests.java
  3. 25
      container-tests/tests/src/test/java/org/hyperledger/besu/tests/container/helpers/ContractOperations.java
  4. 34
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetCode.java
  5. 3
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthJsonRpcMethods.java
  6. 2
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethService.java

@ -42,9 +42,12 @@ import org.web3j.quorum.Quorum;
@SuppressWarnings({"rawtypes", "unchecked"})
public class ContainerTestBase {
// A docker image can be built using gradlew distDocker
// To use that local image change besuImage to "hyperledger/besu:<VERSION>-SNAPSHOT"
// A docker image can be built using `./gradlew clean distDocker`
// check the status of local images with `docker images`
// To use that local image, change besuImage below eg
// private final String besuImage = "hyperledger/besu:21.7.0-RC1-SNAPSHOT";
private final String besuImage = System.getProperty("containertest.imagename");
private final String goQuorumVersion = "21.1.0";
private final String tesseraVersion = "21.1.1";

@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.fail;
import static org.hyperledger.besu.tests.container.helpers.ContractOperations.deployContractAndReturnAddress;
import static org.hyperledger.besu.tests.container.helpers.ContractOperations.generateHexString;
import static org.hyperledger.besu.tests.container.helpers.ContractOperations.getCode;
import static org.hyperledger.besu.tests.container.helpers.ContractOperations.getTransactionLog;
import static org.hyperledger.besu.tests.container.helpers.ContractOperations.sendLogEventAndReturnTransactionHash;
@ -51,6 +52,7 @@ import org.web3j.tx.response.PollingTransactionReceiptProcessor;
public class ContainerTests extends ContainerTestBase {
public static final String CONTRACT_PREFIX = "0x6080604052348015600f57600080fd5b5060";
private Credentials credentials;
private Enclave besuEnclave;
private EnclaveService besuEnclaveService;
@ -120,6 +122,15 @@ public class ContainerTests extends ContainerTestBase {
assertThat(besuResult).isEqualTo(logValue);
assertThat(goQuorumResult).isEqualTo(logValue);
// Assert the Besu node gets value for eth_getCode
final String codeValueBesu = getCode(besuWeb3j, contractAddress);
assertThat(codeValueBesu).startsWith(CONTRACT_PREFIX);
// Assert the GoQuorum node gets value for eth_getCode
final String codeValueGoQuorum = getCode(goQuorumWeb3j, contractAddress);
assertThat(codeValueGoQuorum).startsWith(CONTRACT_PREFIX);
assertThat(codeValueBesu).isEqualTo(codeValueGoQuorum);
}
@Test
@ -164,6 +175,14 @@ public class ContainerTests extends ContainerTestBase {
// Assert the Besu node has not received the log
assertThatThrownBy(() -> getTransactionLog(besuWeb3j, transactionHash))
.hasMessageContaining("No log found");
// Assert the GoQuorum node gets value for eth_getCode
final String codeValueGoQuorum = getCode(goQuorumWeb3j, contractAddress);
assertThat(codeValueGoQuorum).startsWith(CONTRACT_PREFIX);
// Assert the Besu node gets NO value for eth_getCode
final String codeValueBesu = getCode(besuWeb3j, contractAddress);
assertThat(codeValueBesu).isEqualTo("0x");
}
@Test
@ -234,6 +253,14 @@ public class ContainerTests extends ContainerTestBase {
goQuorumPollingTransactionReceiptProcessor,
logValue);
// Assert the GoQuorum node gets NO value for eth_getCode
final String codeValueGoQuorum = getCode(goQuorumWeb3j, contractAddress);
assertThat(codeValueGoQuorum).isEqualTo("0x");
// Assert the Besu node gets a value for eth_getCode
final String codeValueBesu = getCode(besuWeb3j, contractAddress);
assertThat(codeValueBesu).startsWith(CONTRACT_PREFIX);
int secondsWaited = 0;
while (!checked.get()) {
Thread.sleep(1000);

@ -24,7 +24,9 @@ import java.util.List;
import org.web3j.abi.FunctionEncoder;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.EthGetCode;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
@ -36,6 +38,11 @@ import org.web3j.quorum.tx.QuorumTransactionManager;
import org.web3j.tx.response.PollingTransactionReceiptProcessor;
public class ContractOperations {
public static final String SIMPLE_STORAGE_CONTRACT_BYTECODE =
"6080604052348015600f57600080fd5b5060de8061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060275760003560e01c8062f88abf14602c575b600080fd5b605560048036036020811015604057600080fd5b81019080803590602001909291905050506057565b005b3373ffffffffffffffffffffffffffffffffffffffff167f748aa07c80b05bd067e3688dbb79d9f9583cd018be6a589a7c364cacd770e0a2826040518082815260200191505060405180910390a25056fea26469706673582212207df0d3ad8bced04b7bd476cc81a6233c0b575966c29b4af96450313628ee623964736f6c63430007040033";
public static String SIMPLE_STORAGE_CONTRACT_WITH_CONSTRUCTOR;
public static String deployContractAndReturnAddress(
final Quorum quorumWeb3j,
final Credentials credentials,
@ -44,10 +51,9 @@ public class ContractOperations {
final PollingTransactionReceiptProcessor pollingTransactionReceiptProcessorIgnore)
throws IOException, TransactionException {
// Build smart contract transaction
final String simpleStorageContractBytecode =
"6080604052348015600f57600080fd5b5060de8061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060275760003560e01c8062f88abf14602c575b600080fd5b605560048036036020811015604057600080fd5b81019080803590602001909291905050506057565b005b3373ffffffffffffffffffffffffffffffffffffffff167f748aa07c80b05bd067e3688dbb79d9f9583cd018be6a589a7c364cacd770e0a2826040518082815260200191505060405180910390a25056fea26469706673582212207df0d3ad8bced04b7bd476cc81a6233c0b575966c29b4af96450313628ee623964736f6c63430007040033";
final String encodedConstructor = FunctionEncoder.encodeConstructor(Collections.emptyList());
final String binaryAndInitCode = simpleStorageContractBytecode + encodedConstructor;
SIMPLE_STORAGE_CONTRACT_WITH_CONSTRUCTOR =
SIMPLE_STORAGE_CONTRACT_BYTECODE + encodedConstructor;
final RawTransaction contractTransaction =
RawTransaction.createTransaction(
@ -56,7 +62,7 @@ public class ContractOperations {
BigInteger.valueOf(4300000),
"",
BigInteger.ZERO,
binaryAndInitCode);
SIMPLE_STORAGE_CONTRACT_WITH_CONSTRUCTOR);
// Send the signed transaction to quorum
final EthSendTransaction sendContractTransactionResult = qtm.signAndSend(contractTransaction);
@ -75,6 +81,17 @@ public class ContractOperations {
return transactionReceiptResult.getContractAddress();
}
public static String getCode(final Quorum web3j, final String contractAddress)
throws IOException {
DefaultBlockParameter blockParam =
DefaultBlockParameter.valueOf(DefaultBlockParameterName.LATEST.toString());
final EthGetCode codeResult = web3j.ethGetCode(contractAddress, blockParam).send();
assertThat(codeResult.getCode())
.withFailMessage("Code for contractAddress not found.")
.isNotEmpty();
return codeResult.getCode();
}
public static String getTransactionLog(final Quorum web3j, final String transactionHash)
throws IOException {
final EthGetTransactionReceipt transactionReceiptResult =

@ -14,24 +14,40 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import static org.hyperledger.besu.ethereum.goquorum.GoQuorumPrivateStateUtil.getPrivateWorldStateAtBlock;
import org.hyperledger.besu.config.GoQuorumOptions;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameterOrBlockHash;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.core.Account;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import java.util.Optional;
import java.util.function.Supplier;
import org.apache.tuweni.bytes.Bytes;
public class EthGetCode extends AbstractBlockParameterOrBlockHashMethod {
public EthGetCode(final BlockchainQueries blockchainQueries) {
final Optional<PrivacyParameters> privacyParameters;
public EthGetCode(
final BlockchainQueries blockchainQueries,
final Optional<PrivacyParameters> privacyParameters) {
super(blockchainQueries);
this.privacyParameters = privacyParameters;
}
public EthGetCode(final Supplier<BlockchainQueries> blockchainQueries) {
public EthGetCode(
final Supplier<BlockchainQueries> blockchainQueries,
final Optional<PrivacyParameters> privacyParameters) {
super(blockchainQueries);
this.privacyParameters = privacyParameters;
}
@Override
@ -48,6 +64,20 @@ public class EthGetCode extends AbstractBlockParameterOrBlockHashMethod {
@Override
protected String resultByBlockHash(final JsonRpcRequestContext request, final Hash blockHash) {
final Address address = request.getRequiredParameter(0, Address.class);
if (GoQuorumOptions.goQuorumCompatibilityMode && privacyParameters.isPresent()) {
// get from private state if we can
final Optional<BlockHeader> blockHeader =
blockchainQueries.get().getBlockHeaderByHash(blockHash);
if (blockHeader.isPresent()) {
final MutableWorldState privateState =
getPrivateWorldStateAtBlock(
privacyParameters.get().getGoQuorumPrivacyParameters(), blockHeader.get());
final Account privAccount = privateState.get(address);
if (privAccount != null) {
return privAccount.getCode().toHexString();
}
}
}
return getBlockchainQueries().getCode(address, blockHash).map(Bytes::toString).orElse(null);
}
}

@ -71,6 +71,7 @@ import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
public class EthJsonRpcMethods extends ApiGroupJsonRpcMethods {
@ -127,7 +128,7 @@ public class EthJsonRpcMethods extends ApiGroupJsonRpcMethods {
blockchainQueries.getWorldStateArchive(),
protocolSchedule,
privacyParameters)),
new EthGetCode(blockchainQueries),
new EthGetCode(blockchainQueries, Optional.of(privacyParameters)),
new EthGetLogs(blockchainQueries),
new EthGetProof(blockchainQueries),
new EthGetUncleCountByBlockHash(blockchainQueries),

@ -73,7 +73,7 @@ public class RetestethService {
new DebugAccountRange(retestethContext::getBlockchainQueries),
new EthGetBalance(retestethContext::getBlockchainQueries),
new EthGetBlockByHash(retestethContext::getBlockchainQueries, blockResult, true),
new EthGetCode(retestethContext::getBlockchainQueries),
new EthGetCode(retestethContext::getBlockchainQueries, Optional.empty()),
new EthGetTransactionCount(
retestethContext::getBlockchainQueries,
retestethContext::getPendingTransactions,

Loading…
Cancel
Save