DRY onchain management proxies (#2674)

* move onchain privacy group contract read access to OnchainPrivacyGroupContract

Signed-off-by: Taccat Isid <taccatisid@protonmail.com>

* fix crash when onchain group managment contract returns invalid member list

Signed-off-by: Taccat Isid <taccatisid@protonmail.com>

* simplify PrivacyPrecompiledContract.isMining

Signed-off-by: Taccat Isid <taccatisid@protonmail.com>

* Declare some variables final.

Signed-off-by: Taccat Isid <taccatisid@protonmail.com>

Co-authored-by: Stefan Pingel <16143240+pinges@users.noreply.github.com>
Co-authored-by: Sally MacFarlane <sally.macfarlane@consensys.net>
pull/2675/head
taccatisid 3 years ago committed by GitHub
parent 21ac9fe310
commit 031c443fc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 239
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/OnChainPrivacyPrecompiledContract.java
  2. 18
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java
  3. 162
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/OnchainPrivacyGroupContract.java
  4. 55
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/OnChainPrivacyPrecompiledContractTest.java

@ -14,20 +14,17 @@
*/
package org.hyperledger.besu.ethereum.mainnet.precompiles.privacy;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.enclave.Enclave;
import org.hyperledger.besu.enclave.EnclaveClientException;
import org.hyperledger.besu.enclave.types.PrivacyGroup;
import org.hyperledger.besu.enclave.types.ReceiveResponse;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.core.WorldUpdater;
import org.hyperledger.besu.ethereum.privacy.OnchainPrivacyGroupContract;
import org.hyperledger.besu.ethereum.privacy.PrivateStateGenesisAllocator;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
@ -39,13 +36,9 @@ import org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import org.hyperledger.besu.ethereum.vm.OperationTracer;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.data.Restriction;
import org.hyperledger.besu.util.Subscribers;
import java.util.ArrayList;
@ -54,33 +47,18 @@ import java.util.Collections;
import java.util.List;
import java.util.Optional;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContract {
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
// Dummy signature for transactions to not fail being processed.
private static final SECPSignature FAKE_SIGNATURE =
SIGNATURE_ALGORITHM
.get()
.createSignature(
SIGNATURE_ALGORITHM.get().getHalfCurveOrder(),
SIGNATURE_ALGORITHM.get().getHalfCurveOrder(),
(byte) 0);
private static final Logger LOG = LogManager.getLogger();
private final Subscribers<PrivateTransactionObserver> privateTransactionEventObservers =
Subscribers.create();
private static final Logger LOG = LogManager.getLogger();
public OnChainPrivacyPrecompiledContract(
final GasCalculator gasCalculator,
final Enclave enclave,
@ -173,7 +151,6 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
privacyGroupId,
currentBlockHeader.getNumber());
final WorldUpdater publicWorldState = messageFrame.getWorldState();
final Blockchain blockchain = messageFrame.getBlockchain();
if (!canExecute(
@ -181,7 +158,6 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
currentBlockHeader,
privateTransaction,
versionedPrivateTransaction.getVersion(),
publicWorldState,
privacyGroupId,
blockchain,
disposablePrivateState,
@ -238,12 +214,19 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
final ProcessableBlockHeader currentBlockHeader,
final PrivateTransaction privateTransaction,
final Bytes32 version,
final WorldUpdater publicWorldState,
final Bytes32 privacyGroupId,
final Blockchain blockchain,
final MutableWorldState disposablePrivateState,
final WorldUpdater privateWorldStateUpdater,
final Bytes privateFrom) {
final OnchainPrivacyGroupContract onchainPrivacyGroupContract =
new OnchainPrivacyGroupContract(
messageFrame,
currentBlockHeader,
disposablePrivateState,
privateWorldStateUpdater,
privateWorldStateArchive,
privateTransactionProcessor);
final boolean isAddingParticipant =
privateTransaction
@ -252,14 +235,7 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
.startsWith(OnChainGroupManagement.ADD_PARTICIPANTS_METHOD_SIGNATURE.toHexString());
final boolean isPrivacyGroupLocked =
isContractLocked(
messageFrame,
currentBlockHeader,
publicWorldState,
privacyGroupId,
blockchain,
disposablePrivateState,
privateWorldStateUpdater);
isContractLocked(onchainPrivacyGroupContract, privacyGroupId);
if (isAddingParticipant && !isPrivacyGroupLocked) {
LOG.debug(
@ -277,15 +253,7 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
return false;
}
if (!onChainPrivacyGroupVersionMatches(
messageFrame,
currentBlockHeader,
version,
publicWorldState,
privacyGroupId,
blockchain,
disposablePrivateState,
privateWorldStateUpdater)) {
if (!onChainPrivacyGroupVersionMatches(onchainPrivacyGroupContract, privacyGroupId, version)) {
LOG.debug(
"Privacy group version mismatch while trying to execute transaction with commitment {}",
messageFrame.getTransactionHash());
@ -296,13 +264,8 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
isAddingParticipant,
privateTransaction,
privateFrom,
privacyGroupId,
messageFrame,
currentBlockHeader,
publicWorldState,
blockchain,
disposablePrivateState,
privateWorldStateUpdater)) {
onchainPrivacyGroupContract,
privacyGroupId)) {
LOG.debug(
"PrivateTransaction with hash {} cannot execute in privacy group {} because privateFrom"
+ " {} is not a member.",
@ -319,43 +282,23 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
final boolean isAddingParticipant,
final PrivateTransaction privateTransaction,
final Bytes privateFrom,
final Bytes32 privacyGroupId,
final MessageFrame messageFrame,
final ProcessableBlockHeader currentBlockHeader,
final WorldUpdater publicWorldState,
final Blockchain blockchain,
final MutableWorldState disposablePrivateState,
final WorldUpdater privateWorldStateUpdater) {
final TransactionProcessingResult result =
simulateTransaction(
messageFrame,
currentBlockHeader,
publicWorldState,
privacyGroupId,
blockchain,
disposablePrivateState,
privateWorldStateUpdater,
OnChainGroupManagement.GET_PARTICIPANTS_METHOD_SIGNATURE);
final List<Bytes> list = getMembersFromResult(result);
final OnchainPrivacyGroupContract onchainPrivacyGroupContract,
final Bytes32 privacyGroupId) {
final List<String> members =
onchainPrivacyGroupContract
.getPrivacyGroupByIdAndBlockHash(privacyGroupId.toBase64String(), Optional.empty())
.map(PrivacyGroup::getMembers)
.orElse(Collections.<String>emptyList());
List<String> participantsFromParameter = Collections.emptyList();
if (list.isEmpty() && isAddingParticipant) {
if (members.isEmpty() && isAddingParticipant) {
// creating a new group, so we are checking whether the privateFrom is one of the members of
// the new group
participantsFromParameter = getParticipantsFromParameter(privateTransaction.getPayload());
}
return list.contains(privateFrom)
|| participantsFromParameter.contains(privateFrom.toBase64String());
}
List<Bytes> getMembersFromResult(final TransactionProcessingResult result) {
List<Bytes> list = Collections.emptyList();
if (result != null && result.isSuccessful()) {
final RLPInput rlpInput = RLP.input(result.getOutput());
if (rlpInput.nextSize() > 0) {
list = decodeList(rlpInput.raw());
}
}
return list;
final String base64privateFrom = privateFrom.toBase64String();
return members.contains(base64privateFrom)
|| participantsFromParameter.contains(base64privateFrom);
}
// TODO: this method is copied from DefaultPrivacyController. Fix the duplication in a separate GI
@ -372,122 +315,32 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
return input.slice(4).toBase64String();
}
private List<Bytes> decodeList(final Bytes rlpEncodedList) {
final ArrayList<Bytes> decodedElements = new ArrayList<>();
// first 32 bytes is dynamic list offset
final UInt256 lengthOfList = UInt256.fromBytes(rlpEncodedList.slice(32, 32)); // length of list
for (int i = 0; i < lengthOfList.toLong(); ++i) {
decodedElements.add(Bytes.wrap(rlpEncodedList.slice(64 + (32 * i), 32))); // participant
}
return decodedElements;
}
protected boolean isContractLocked(
final MessageFrame messageFrame,
final ProcessableBlockHeader currentBlockHeader,
final WorldUpdater publicWorldState,
final Bytes32 privacyGroupId,
final Blockchain blockchain,
final MutableWorldState disposablePrivateState,
final WorldUpdater privateWorldStateUpdater) {
final TransactionProcessingResult result =
simulateTransaction(
messageFrame,
currentBlockHeader,
publicWorldState,
privacyGroupId,
blockchain,
disposablePrivateState,
privateWorldStateUpdater,
OnChainGroupManagement.CAN_EXECUTE_METHOD_SIGNATURE);
return result.getOutput().toHexString().endsWith("0");
}
protected TransactionProcessingResult simulateTransaction(
final MessageFrame messageFrame,
final ProcessableBlockHeader currentBlockHeader,
final WorldUpdater publicWorldState,
final Bytes32 privacyGroupId,
final Blockchain currentBlockchain,
final MutableWorldState disposablePrivateState,
final WorldUpdater privateWorldStateUpdater,
final Bytes methodSignature) {
// We need the "lock status" of the group for every single transaction but we don't want this
// call to affect the state
// privateTransactionProcessor.processTransaction(...) commits the state if the process was
// successful before it returns
final MutableWorldState localMutableState =
privateWorldStateArchive.getMutable(disposablePrivateState.rootHash(), null).get();
final WorldUpdater updater = localMutableState.updater();
return privateTransactionProcessor.processTransaction(
currentBlockchain,
publicWorldState,
updater,
currentBlockHeader,
messageFrame.getTransactionHash(),
buildSimulationTransaction(privacyGroupId, privateWorldStateUpdater, methodSignature),
messageFrame.getMiningBeneficiary(),
OperationTracer.NO_TRACING,
messageFrame.getBlockHashLookup(),
privacyGroupId);
final OnchainPrivacyGroupContract onchainPrivacyGroupContract, final Bytes32 privacyGroupId) {
final Optional<Bytes32> canExecuteResult =
onchainPrivacyGroupContract.getCanExecute(
privacyGroupId.toBase64String(), Optional.empty());
final boolean isLocked = canExecuteResult.map(Bytes::isZero).orElse(true);
return isLocked;
}
protected boolean onChainPrivacyGroupVersionMatches(
final MessageFrame messageFrame,
final ProcessableBlockHeader currentBlockHeader,
final Bytes32 version,
final WorldUpdater publicWorldState,
final OnchainPrivacyGroupContract onchainPrivacyGroupContract,
final Bytes32 privacyGroupId,
final Blockchain currentBlockchain,
final MutableWorldState disposablePrivateState,
final WorldUpdater privateWorldStateUpdater) {
// We need the "version" of the group for every single transaction but we don't want this
// call to affect the state
// privateTransactionProcessor.processTransaction(...) commits the state if the process was
// successful before it returns
final TransactionProcessingResult getVersionResult =
simulateTransaction(
messageFrame,
currentBlockHeader,
publicWorldState,
privacyGroupId,
currentBlockchain,
disposablePrivateState,
privateWorldStateUpdater,
OnChainGroupManagement.GET_VERSION_METHOD_SIGNATURE);
final Bytes32 version) {
final Optional<Bytes32> contractVersionResult =
onchainPrivacyGroupContract.getVersion(privacyGroupId.toBase64String(), Optional.empty());
final boolean versionEqual =
contractVersionResult.map(contractVersion -> version.equals(contractVersion)).orElse(false);
if (version.equals(getVersionResult.getOutput())) {
return true;
if (!versionEqual) {
LOG.debug(
"Privacy Group {} version mismatch: expecting {} but got {}",
privacyGroupId.toBase64String(),
contractVersionResult,
Optional.of(version));
}
LOG.debug(
"Privacy Group {} version mismatch for commitment {}: expecting {} but got {}",
privacyGroupId.toBase64String(),
messageFrame.getTransactionHash(),
getVersionResult.getOutput(),
version);
return false;
}
private PrivateTransaction buildSimulationTransaction(
final Bytes privacyGroupId,
final WorldUpdater privateWorldStateUpdater,
final Bytes payload) {
return PrivateTransaction.builder()
.privateFrom(Bytes.EMPTY)
.privacyGroupId(privacyGroupId)
.restriction(Restriction.RESTRICTED)
.nonce(
privateWorldStateUpdater.getAccount(Address.ZERO) != null
? privateWorldStateUpdater.getAccount(Address.ZERO).getNonce()
: 0)
.gasPrice(Wei.of(1000))
.gasLimit(3000000)
.to(Address.ONCHAIN_PRIVACY_PROXY)
.sender(Address.ZERO)
.value(Wei.ZERO)
.payload(payload)
.signature(FAKE_SIGNATURE)
.build();
return versionEqual;
}
}

@ -270,17 +270,15 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
}
boolean isMining(final MessageFrame messageFrame) {
boolean isMining = false;
final ProcessableBlockHeader currentBlockHeader = messageFrame.getBlockHeader();
if (!BlockHeader.class.isAssignableFrom(currentBlockHeader.getClass())) {
if (!messageFrame.isPersistingPrivateState()) {
isMining = true;
} else {
throw new IllegalArgumentException(
"The MessageFrame contains an illegal block header type. Cannot persist private block"
+ " metadata without current block hash.");
}
if (BlockHeader.class.isAssignableFrom(currentBlockHeader.getClass())) {
return false;
}
if (messageFrame.isPersistingPrivateState()) {
throw new IllegalArgumentException(
"The MessageFrame contains an illegal block header type. Cannot persist private block"
+ " metadata without current block hash.");
}
return isMining;
return true;
}
}

@ -14,37 +14,118 @@
*/
package org.hyperledger.besu.ethereum.privacy;
import static org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement.CAN_EXECUTE_METHOD_SIGNATURE;
import static org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement.GET_PARTICIPANTS_METHOD_SIGNATURE;
import static org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement.GET_VERSION_METHOD_SIGNATURE;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.enclave.types.PrivacyGroup;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.core.WorldUpdater;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.ethereum.transaction.CallParameter;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import org.hyperledger.besu.ethereum.vm.OperationTracer;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.data.Restriction;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
/*
This contract is an abstraction on top of the privacy group management smart contract.
TODO: there are a few places on OnChainPrivacyPrecompiledContract that we could use this class instead of a transaction simulator there.
This class is an abstraction on top of the privacy group management smart contract.
It is possible to use it in two different ways that carry different
lifetime expectations and call semantics:
1. When constructed using `OnchainPrivacyGroupContract(PrivateTransactionSimulator)`
the object is expected to be long-lived. Methods can be supplied
with block height or block hash parameters to select which block's
state is queried.
2. When using the alternative constructor, no height or hash
parameter must be supplied to subsequent method calls. All methods
operate on the state specified by the given MessageFrame. Only
when constructed this way, the class can be used to query state
that is not on a block boundary. Used this way, the object's life
time is intended to be short.
*/
public class OnchainPrivacyGroupContract {
@FunctionalInterface
public interface TransactionSimulator {
Optional<TransactionProcessingResult> simulate(
final String privacyGroupId,
final Bytes callData,
final Optional<Hash> blockHash,
final Optional<Long> blockNumber);
}
private final PrivateTransactionSimulator privateTransactionSimulator;
final TransactionSimulator transactionSimulator;
public OnchainPrivacyGroupContract(
final PrivateTransactionSimulator privateTransactionSimulator) {
this.privateTransactionSimulator = privateTransactionSimulator;
transactionSimulator =
(privacyGroupId, callData, blockHash, blockNumber) -> {
final CallParameter callParameter = buildCallParams(callData);
if (blockHash.isPresent()) {
return privateTransactionSimulator.process(
privacyGroupId, callParameter, blockHash.get());
} else if (blockNumber.isPresent()) {
return privateTransactionSimulator.process(
privacyGroupId, callParameter, blockNumber.get());
} else {
return privateTransactionSimulator.process(privacyGroupId, callParameter);
}
};
}
public OnchainPrivacyGroupContract(
final MessageFrame messageFrame,
final ProcessableBlockHeader currentBlockHeader,
final MutableWorldState disposablePrivateState,
final WorldUpdater privateWorldStateUpdater,
final WorldStateArchive privateWorldStateArchive,
final PrivateTransactionProcessor privateTransactionProcessor) {
transactionSimulator =
(base64privacyGroupId, callData, blockHash, blockNumber) -> {
assert !blockHash.isPresent();
assert !blockNumber.isPresent();
final Bytes privacyGroupId = Bytes.fromBase64String(base64privacyGroupId);
final MutableWorldState localMutableState =
privateWorldStateArchive.getMutable(disposablePrivateState.rootHash(), null).get();
final WorldUpdater updater = localMutableState.updater();
final PrivateTransaction privateTransaction =
buildTransaction(privacyGroupId, privateWorldStateUpdater, callData);
return Optional.of(
privateTransactionProcessor.processTransaction(
messageFrame.getBlockchain(),
messageFrame.getWorldState(),
updater,
currentBlockHeader,
messageFrame.getTransactionHash(),
privateTransaction,
messageFrame.getMiningBeneficiary(),
OperationTracer.NO_TRACING,
messageFrame.getBlockHashLookup(),
privacyGroupId));
};
}
public Optional<PrivacyGroup> getPrivacyGroupById(final String privacyGroupId) {
@ -66,16 +147,9 @@ public class OnchainPrivacyGroupContract {
final Optional<Hash> blockHash,
final Optional<Long> blockNumber) {
final CallParameter callParams = buildCallParams(GET_PARTICIPANTS_METHOD_SIGNATURE);
final Optional<TransactionProcessingResult> result;
if (blockHash.isPresent()) {
result = privateTransactionSimulator.process(privacyGroupId, callParams, blockHash.get());
} else if (blockNumber.isPresent()) {
result = privateTransactionSimulator.process(privacyGroupId, callParams, blockNumber.get());
} else {
result = privateTransactionSimulator.process(privacyGroupId, callParams);
}
final Optional<TransactionProcessingResult> result =
transactionSimulator.simulate(
privacyGroupId, GET_PARTICIPANTS_METHOD_SIGNATURE, blockHash, blockNumber);
return readPrivacyGroupFromResult(privacyGroupId, result);
}
@ -101,18 +175,54 @@ public class OnchainPrivacyGroupContract {
}
public Optional<Bytes32> getVersion(final String privacyGroupId, final Optional<Hash> blockHash) {
final CallParameter callParams = buildCallParams(GET_VERSION_METHOD_SIGNATURE);
final Optional<TransactionProcessingResult> result;
if (blockHash.isPresent()) {
result = privateTransactionSimulator.process(privacyGroupId, callParams, blockHash.get());
} else {
result = privateTransactionSimulator.process(privacyGroupId, callParams);
}
final Optional<TransactionProcessingResult> result =
transactionSimulator.simulate(
privacyGroupId, GET_VERSION_METHOD_SIGNATURE, blockHash, Optional.empty());
return result.map(TransactionProcessingResult::getOutput).map(Bytes32::wrap);
}
public Optional<Bytes32> getCanExecute(
final String privacyGroupId, final Optional<Hash> blockHash) {
final Optional<TransactionProcessingResult> result =
transactionSimulator.simulate(
privacyGroupId, CAN_EXECUTE_METHOD_SIGNATURE, blockHash, Optional.empty());
return result.map(TransactionProcessingResult::getOutput).map(Bytes32::wrap);
}
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
// Dummy signature for transactions to not fail being processed.
private static final SECPSignature FAKE_SIGNATURE =
SIGNATURE_ALGORITHM
.get()
.createSignature(
SIGNATURE_ALGORITHM.get().getHalfCurveOrder(),
SIGNATURE_ALGORITHM.get().getHalfCurveOrder(),
(byte) 0);
private PrivateTransaction buildTransaction(
final Bytes privacyGroupId,
final WorldUpdater privateWorldStateUpdater,
final Bytes payload) {
return PrivateTransaction.builder()
.privateFrom(Bytes.EMPTY)
.privacyGroupId(privacyGroupId)
.restriction(Restriction.RESTRICTED)
.nonce(
privateWorldStateUpdater.getAccount(Address.ZERO) != null
? privateWorldStateUpdater.getAccount(Address.ZERO).getNonce()
: 0)
.gasPrice(Wei.of(1000))
.gasLimit(3000000)
.to(Address.ONCHAIN_PRIVACY_PROXY)
.sender(Address.ZERO)
.value(Wei.ZERO)
.payload(payload)
.signature(FAKE_SIGNATURE)
.build();
}
private CallParameter buildCallParams(final Bytes methodCall) {
return new CallParameter(
Address.ZERO, Address.ONCHAIN_PRIVACY_PROXY, 3000000, Wei.of(1000), Wei.ZERO, methodCall);
@ -121,8 +231,12 @@ public class OnchainPrivacyGroupContract {
private List<String> decodeList(final Bytes rlpEncodedList) {
final ArrayList<String> decodedElements = new ArrayList<>();
// first 32 bytes is dynamic list offset
final UInt256 lengthOfList = UInt256.fromBytes(rlpEncodedList.slice(32, 32)); // length of list
for (int i = 0; i < lengthOfList.toLong(); ++i) {
if (rlpEncodedList.size() < 64) return decodedElements;
final long lengthOfList =
UInt256.fromBytes(rlpEncodedList.slice(32, 32)).toLong(); // length of list
if (rlpEncodedList.size() < 64 + lengthOfList * 32) return decodedElements;
for (int i = 0; i < lengthOfList; ++i) {
decodedElements.add(
Bytes.wrap(rlpEncodedList.slice(64 + (32 * i), 32)).toBase64String()); // participant
}

@ -57,7 +57,6 @@ import org.hyperledger.besu.ethereum.vm.OperationTracer;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@ -168,7 +167,7 @@ public class OnChainPrivacyPrecompiledContractTest {
final OnChainPrivacyPrecompiledContract contractSpy = spy(contract);
Mockito.doReturn(true)
.when(contractSpy)
.canExecute(any(), any(), any(), any(), any(), any(), any(), any(), any(), any());
.canExecute(any(), any(), any(), any(), any(), any(), any(), any(), any());
final Bytes actual = contractSpy.compute(privateTransactionLookupId, messageFrame);
@ -234,22 +233,46 @@ public class OnChainPrivacyPrecompiledContractTest {
@Test
public void testPrivateFromNotMemberOfGroup() {
// array length too big
assertThatComputeReturnsEmptyGivenContractMembershipQueryReturns(
Bytes.concatenate(
Bytes32.fromHexStringLenient("0x0"),
// array length
Bytes32.fromHexStringLenient("0x1"),
// first array content
Bytes32.fromHexStringLenient("0x1")));
}
@Test
public void testInvalidResponseToMembershipQuery() {
// response shorter than empty array response
assertThatComputeReturnsEmptyGivenContractMembershipQueryReturns(
Bytes32.fromHexStringLenient("0x0"));
// array length too big
assertThatComputeReturnsEmptyGivenContractMembershipQueryReturns(
Bytes.concatenate(
// offset to start of array
Bytes32.fromHexStringLenient("0x0"),
// array length
Bytes32.fromHexStringLenient("0x2"),
// first array content
Bytes32.fromHexStringLenient("0x1")));
}
private void assertThatComputeReturnsEmptyGivenContractMembershipQueryReturns(
final Bytes memberList) {
final Enclave enclave = mock(Enclave.class);
final OnChainPrivacyPrecompiledContract contract = buildPrivacyPrecompiledContract(enclave);
final List<Log> logs = new ArrayList<>();
contract.setPrivateTransactionProcessor(
mockPrivateTxProcessor(
TransactionProcessingResult.successful(logs, 0, 0, memberList, null)));
final OnChainPrivacyPrecompiledContract contractSpy = spy(contract);
Mockito.doReturn(false)
.when(contractSpy)
.isContractLocked(any(), any(), any(), any(), any(), any(), any());
Mockito.doReturn(true)
.when(contractSpy)
.onChainPrivacyGroupVersionMatches(any(), any(), any(), any(), any(), any(), any(), any());
final TransactionProcessingResult mockResult = mock(TransactionProcessingResult.class);
Mockito.doReturn(mockResult)
.when(contractSpy)
.simulateTransaction(any(), any(), any(), any(), any(), any(), any(), any());
Mockito.doReturn(Arrays.asList(Bytes.ofUnsignedInt(1L)))
.when(contractSpy)
.getMembersFromResult(any());
Mockito.doReturn(false).when(contractSpy).isContractLocked(any(), any());
Mockito.doReturn(true).when(contractSpy).onChainPrivacyGroupVersionMatches(any(), any(), any());
final VersionedPrivateTransaction privateTransaction = versionedPrivateTransactionBesu();
final byte[] payload = convertVersionedPrivateTransactionToBytes(privateTransaction);
@ -285,7 +308,7 @@ public class OnChainPrivacyPrecompiledContractTest {
final OnChainPrivacyPrecompiledContract contractSpy = spy(contract);
Mockito.doReturn(true)
.when(contractSpy)
.canExecute(any(), any(), any(), any(), any(), any(), any(), any(), any(), any());
.canExecute(any(), any(), any(), any(), any(), any(), any(), any(), any());
final VersionedPrivateTransaction privateTransaction = versionedPrivateTransactionBesu();
final byte[] payload = convertVersionedPrivateTransactionToBytes(privateTransaction);

Loading…
Cancel
Save