eth_call now supports GoQuorum private transactions (#1934)

eth_call now supports GoQuorum private transactions

Signed-off-by: Sally MacFarlane <sally.macfarlane@consensys.net>
pull/1965/head
Sally MacFarlane 4 years ago committed by GitHub
parent f25d458345
commit 711bbfbd5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthJsonRpcMethods.java
  2. 3
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java
  3. 34
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockValidator.java
  4. 71
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumPrivateStateUtil.java
  5. 56
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java
  6. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java
  7. 44
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/GoQuorumMutablePrivateAndPublicWorldStateUpdater.java

@ -63,6 +63,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator; import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.core.Synchronizer;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -83,6 +84,7 @@ public class EthJsonRpcMethods extends ApiGroupJsonRpcMethods {
private final TransactionPool transactionPool; private final TransactionPool transactionPool;
private final MiningCoordinator miningCoordinator; private final MiningCoordinator miningCoordinator;
private final Set<Capability> supportedCapabilities; private final Set<Capability> supportedCapabilities;
private final PrivacyParameters privacyParameters;
public EthJsonRpcMethods( public EthJsonRpcMethods(
final BlockchainQueries blockchainQueries, final BlockchainQueries blockchainQueries,
@ -91,7 +93,8 @@ public class EthJsonRpcMethods extends ApiGroupJsonRpcMethods {
final FilterManager filterManager, final FilterManager filterManager,
final TransactionPool transactionPool, final TransactionPool transactionPool,
final MiningCoordinator miningCoordinator, final MiningCoordinator miningCoordinator,
final Set<Capability> supportedCapabilities) { final Set<Capability> supportedCapabilities,
final PrivacyParameters privacyParameters) {
this.blockchainQueries = blockchainQueries; this.blockchainQueries = blockchainQueries;
this.synchronizer = synchronizer; this.synchronizer = synchronizer;
this.protocolSchedule = protocolSchedule; this.protocolSchedule = protocolSchedule;
@ -99,6 +102,7 @@ public class EthJsonRpcMethods extends ApiGroupJsonRpcMethods {
this.transactionPool = transactionPool; this.transactionPool = transactionPool;
this.miningCoordinator = miningCoordinator; this.miningCoordinator = miningCoordinator;
this.supportedCapabilities = supportedCapabilities; this.supportedCapabilities = supportedCapabilities;
this.privacyParameters = privacyParameters;
} }
@Override @Override
@ -121,7 +125,8 @@ public class EthJsonRpcMethods extends ApiGroupJsonRpcMethods {
new TransactionSimulator( new TransactionSimulator(
blockchainQueries.getBlockchain(), blockchainQueries.getBlockchain(),
blockchainQueries.getWorldStateArchive(), blockchainQueries.getWorldStateArchive(),
protocolSchedule)), protocolSchedule,
privacyParameters)),
new EthGetCode(blockchainQueries), new EthGetCode(blockchainQueries),
new EthGetLogs(blockchainQueries), new EthGetLogs(blockchainQueries),
new EthGetProof(blockchainQueries), new EthGetProof(blockchainQueries),
@ -149,7 +154,8 @@ public class EthJsonRpcMethods extends ApiGroupJsonRpcMethods {
new TransactionSimulator( new TransactionSimulator(
blockchainQueries.getBlockchain(), blockchainQueries.getBlockchain(),
blockchainQueries.getWorldStateArchive(), blockchainQueries.getWorldStateArchive(),
protocolSchedule)), protocolSchedule,
privacyParameters)),
new EthMining(miningCoordinator), new EthMining(miningCoordinator),
new EthCoinbase(miningCoordinator), new EthCoinbase(miningCoordinator),
new EthProtocolVersion(supportedCapabilities), new EthProtocolVersion(supportedCapabilities),

@ -102,7 +102,8 @@ public class JsonRpcMethodsFactory {
filterManager, filterManager,
transactionPool, transactionPool,
miningCoordinator, miningCoordinator,
supportedCapabilities), supportedCapabilities,
privacyParameters),
new NetJsonRpcMethods( new NetJsonRpcMethods(
p2pNetwork, p2pNetwork,
networkId, networkId,

@ -14,14 +14,13 @@
*/ */
package org.hyperledger.besu.ethereum.goquorum; package org.hyperledger.besu.ethereum.goquorum;
import static org.apache.logging.log4j.LogManager.getLogger; import static org.hyperledger.besu.ethereum.goquorum.GoQuorumPrivateStateUtil.getPrivateWorldState;
import org.hyperledger.besu.ethereum.MainnetBlockValidator; import org.hyperledger.besu.ethereum.MainnetBlockValidator;
import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.BadBlockManager; import org.hyperledger.besu.ethereum.chain.BadBlockManager;
import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters; import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.mainnet.BlockBodyValidator; import org.hyperledger.besu.ethereum.mainnet.BlockBodyValidator;
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator; import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
@ -31,12 +30,8 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import java.util.Optional; import java.util.Optional;
import org.apache.logging.log4j.Logger;
public class GoQuorumBlockValidator extends MainnetBlockValidator { public class GoQuorumBlockValidator extends MainnetBlockValidator {
private static final Logger LOG = getLogger();
private final GoQuorumPrivateStorage goQuorumPrivateStorage; private final GoQuorumPrivateStorage goQuorumPrivateStorage;
private final WorldStateArchive goQuorumWorldStateArchive; private final WorldStateArchive goQuorumWorldStateArchive;
@ -66,34 +61,9 @@ public class GoQuorumBlockValidator extends MainnetBlockValidator {
protected Result processBlock( protected Result processBlock(
final ProtocolContext context, final MutableWorldState worldState, final Block block) { final ProtocolContext context, final MutableWorldState worldState, final Block block) {
final MutableWorldState privateWorldState = final MutableWorldState privateWorldState =
getPrivateWorldState(worldState.rootHash(), block.getHash()); getPrivateWorldState(goQuorumPrivateStorage, goQuorumWorldStateArchive, block.getHeader());
return ((GoQuorumBlockProcessor) blockProcessor) return ((GoQuorumBlockProcessor) blockProcessor)
.processBlock(context.getBlockchain(), worldState, privateWorldState, block); .processBlock(context.getBlockchain(), worldState, privateWorldState, block);
} }
private MutableWorldState getPrivateWorldState(
final Hash worldStateRootHash, final Hash publicBlockHash) {
final Hash privateStateRootHash =
goQuorumPrivateStorage
.getPrivateStateRootHash(worldStateRootHash)
.orElse(Hash.EMPTY_TRIE_HASH);
final Optional<MutableWorldState> maybePrivateWorldState =
goQuorumWorldStateArchive.getMutable(privateStateRootHash, publicBlockHash);
if (maybePrivateWorldState.isEmpty()) {
LOG.debug(
"Private world state not available for public world state root hash {}, public block hash {}",
worldStateRootHash,
publicBlockHash);
/*
This should never happen because privateStateRootResolver will either return a matching
private world state root hash, or the hash for an empty world state (first private tx ever).
*/
throw new IllegalStateException(
"Private world state not available for public world state root hash " + publicBlockHash);
}
return maybePrivateWorldState.get();
}
} }

@ -0,0 +1,71 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.goquorum;
import static org.apache.logging.log4j.LogManager.getLogger;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import java.util.Optional;
import org.apache.logging.log4j.Logger;
public class GoQuorumPrivateStateUtil {
private static final Logger LOG = getLogger();
public static MutableWorldState getPrivateWorldState(
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters,
final BlockHeader header) {
final GoQuorumPrivateStorage goQuorumPrivateStorage =
goQuorumPrivacyParameters.orElseThrow().privateStorage();
final WorldStateArchive goQuorumWorldStateArchive =
goQuorumPrivacyParameters.orElseThrow().worldStateArchive();
return getPrivateWorldState(goQuorumPrivateStorage, goQuorumWorldStateArchive, header);
}
public static MutableWorldState getPrivateWorldState(
final GoQuorumPrivateStorage goQuorumPrivateStorage,
final WorldStateArchive goQuorumWorldStateArchive,
final BlockHeader header) {
final Hash worldStateRootHash = header.getStateRoot();
final Hash publicBlockHash = header.getHash();
final Hash privateStateRootHash =
goQuorumPrivateStorage
.getPrivateStateRootHash(worldStateRootHash)
.orElse(Hash.EMPTY_TRIE_HASH);
final Optional<MutableWorldState> maybePrivateWorldState =
goQuorumWorldStateArchive.getMutable(privateStateRootHash, publicBlockHash);
if (maybePrivateWorldState.isEmpty()) {
LOG.debug(
"Private world state not available for public world state root hash {}, public block hash {}",
worldStateRootHash,
publicBlockHash);
/*
This should never happen because privateStateRootResolver will either return a matching
private world state root hash, or the hash for an empty world state (first private tx ever).
*/
throw new IllegalStateException(
"Private world state not available for public world state root hash " + publicBlockHash);
}
return maybePrivateWorldState.get();
}
}

@ -14,6 +14,8 @@
*/ */
package org.hyperledger.besu.ethereum.transaction; package org.hyperledger.besu.ethereum.transaction;
import static org.hyperledger.besu.ethereum.goquorum.GoQuorumPrivateStateUtil.getPrivateWorldState;
import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm; import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
@ -23,6 +25,7 @@ import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.core.WorldUpdater; import org.hyperledger.besu.ethereum.core.WorldUpdater;
@ -33,6 +36,7 @@ import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.OperationTracer; import org.hyperledger.besu.ethereum.vm.OperationTracer;
import org.hyperledger.besu.ethereum.worldstate.GoQuorumMutablePrivateAndPublicWorldStateUpdater;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.data.TransactionType; import org.hyperledger.besu.plugin.data.TransactionType;
@ -50,7 +54,6 @@ import org.apache.tuweni.units.bigints.UInt256;
* blockchain or to estimate the transaction gas cost. * blockchain or to estimate the transaction gas cost.
*/ */
public class TransactionSimulator { public class TransactionSimulator {
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM = private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance); Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
@ -71,6 +74,7 @@ public class TransactionSimulator {
private final Blockchain blockchain; private final Blockchain blockchain;
private final WorldStateArchive worldStateArchive; private final WorldStateArchive worldStateArchive;
private final ProtocolSchedule protocolSchedule; private final ProtocolSchedule protocolSchedule;
private final Optional<PrivacyParameters> maybePrivacyParameters;
public TransactionSimulator( public TransactionSimulator(
final Blockchain blockchain, final Blockchain blockchain,
@ -79,6 +83,18 @@ public class TransactionSimulator {
this.blockchain = blockchain; this.blockchain = blockchain;
this.worldStateArchive = worldStateArchive; this.worldStateArchive = worldStateArchive;
this.protocolSchedule = protocolSchedule; this.protocolSchedule = protocolSchedule;
this.maybePrivacyParameters = Optional.empty();
}
public TransactionSimulator(
final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final ProtocolSchedule protocolSchedule,
final PrivacyParameters privacyParameters) {
this.blockchain = blockchain;
this.worldStateArchive = worldStateArchive;
this.protocolSchedule = protocolSchedule;
this.maybePrivacyParameters = Optional.of(privacyParameters);
} }
public Optional<TransactionSimulatorResult> process( public Optional<TransactionSimulatorResult> process(
@ -125,15 +141,18 @@ public class TransactionSimulator {
if (header == null) { if (header == null) {
return Optional.empty(); return Optional.empty();
} }
final MutableWorldState worldState =
final MutableWorldState publicWorldState =
worldStateArchive.getMutable(header.getStateRoot(), header.getHash()).orElse(null); worldStateArchive.getMutable(header.getStateRoot(), header.getHash()).orElse(null);
if (worldState == null) {
if (publicWorldState == null) {
return Optional.empty(); return Optional.empty();
} }
final WorldUpdater updater = getEffectiveWorldStateUpdater(header, publicWorldState);
final Address senderAddress = final Address senderAddress =
callParams.getFrom() != null ? callParams.getFrom() : DEFAULT_FROM; callParams.getFrom() != null ? callParams.getFrom() : DEFAULT_FROM;
final Account sender = worldState.get(senderAddress); final Account sender = publicWorldState.get(senderAddress);
final long nonce = sender != null ? sender.getNonce() : 0L; final long nonce = sender != null ? sender.getNonce() : 0L;
final long gasLimit = final long gasLimit =
callParams.getGasLimit() >= 0 ? callParams.getGasLimit() : header.getGasLimit(); callParams.getGasLimit() >= 0 ? callParams.getGasLimit() : header.getGasLimit();
@ -141,8 +160,6 @@ public class TransactionSimulator {
final Wei value = callParams.getValue() != null ? callParams.getValue() : Wei.ZERO; final Wei value = callParams.getValue() != null ? callParams.getValue() : Wei.ZERO;
final Bytes payload = callParams.getPayload() != null ? callParams.getPayload() : Bytes.EMPTY; final Bytes payload = callParams.getPayload() != null ? callParams.getPayload() : Bytes.EMPTY;
final WorldUpdater updater = worldState.updater();
if (transactionValidationParams.isAllowExceedingBalance()) { if (transactionValidationParams.isAllowExceedingBalance()) {
updater.getOrCreate(senderAddress).getMutable().setBalance(Wei.of(UInt256.MAX_VALUE)); updater.getOrCreate(senderAddress).getMutable().setBalance(Wei.of(UInt256.MAX_VALUE));
} }
@ -182,17 +199,34 @@ public class TransactionSimulator {
return Optional.of(new TransactionSimulatorResult(transaction, result)); return Optional.of(new TransactionSimulatorResult(transaction, result));
} }
// return combined private/public world state updater if GoQuorum mode, otherwise the public state
private WorldUpdater getEffectiveWorldStateUpdater(
final BlockHeader header, final MutableWorldState publicWorldState) {
if (maybePrivacyParameters.isPresent()
&& maybePrivacyParameters.get().getGoQuorumPrivacyParameters().isPresent()) {
final MutableWorldState privateWorldState =
getPrivateWorldState(maybePrivacyParameters.get().getGoQuorumPrivacyParameters(), header);
return new GoQuorumMutablePrivateAndPublicWorldStateUpdater(
publicWorldState.updater(), privateWorldState.updater());
}
return publicWorldState.updater();
}
public Optional<Boolean> doesAddressExistAtHead(final Address address) { public Optional<Boolean> doesAddressExistAtHead(final Address address) {
return doesAddressExist(address, blockchain.getChainHeadHeader()); final BlockHeader header = blockchain.getChainHeadHeader();
final MutableWorldState worldState =
worldStateArchive.getMutable(header.getStateRoot(), header.getHash()).orElse(null);
return doesAddressExist(worldState, address, header);
} }
public Optional<Boolean> doesAddressExist(final Address address, final BlockHeader header) { public Optional<Boolean> doesAddressExist(
final MutableWorldState worldState, final Address address, final BlockHeader header) {
if (header == null) { if (header == null) {
return Optional.empty(); return Optional.empty();
} }
final MutableWorldState worldState =
worldStateArchive.getMutable(header.getStateRoot(), header.getHash()).orElse(null);
if (worldState == null) { if (worldState == null) {
return Optional.empty(); return Optional.empty();
} }

@ -30,7 +30,7 @@ import java.util.Optional;
public class DefaultMutablePrivateWorldStateUpdater implements WorldUpdater { public class DefaultMutablePrivateWorldStateUpdater implements WorldUpdater {
protected final WorldUpdater publicWorldUpdater; protected final WorldUpdater publicWorldUpdater;
private final WorldUpdater privateWorldUpdater; protected final WorldUpdater privateWorldUpdater;
public DefaultMutablePrivateWorldStateUpdater( public DefaultMutablePrivateWorldStateUpdater(
final WorldUpdater publicWorldUpdater, final WorldUpdater privateWorldUpdater) { final WorldUpdater publicWorldUpdater, final WorldUpdater privateWorldUpdater) {

@ -0,0 +1,44 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.worldstate;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.EvmAccount;
import org.hyperledger.besu.ethereum.core.WorldUpdater;
// This class uses a public WorldUpdater and a private WorldUpdater to provide a
// MutableWorldStateUpdater that can read and write from BOTH the private world state and the public
// world state.
public class GoQuorumMutablePrivateAndPublicWorldStateUpdater
extends GoQuorumMutablePrivateWorldStateUpdater {
public GoQuorumMutablePrivateAndPublicWorldStateUpdater(
final WorldUpdater publicWorldUpdater, final WorldUpdater privateWorldUpdater) {
super(publicWorldUpdater, privateWorldUpdater);
}
@Override
public EvmAccount getAccount(final Address address) {
final EvmAccount privateAccount = privateWorldUpdater.getAccount(address);
if (privateAccount != null && !privateAccount.isEmpty()) {
return privateAccount;
}
final EvmAccount publicAccount = publicWorldUpdater.getAccount(address);
if (publicAccount != null && !publicAccount.isEmpty()) {
return publicAccount;
}
return privateAccount;
}
}
Loading…
Cancel
Save