[Privacy] Allow users to provide a private genesis (#2509)

* Refactor: PrivacyBlockProcessor to clarify intent

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>

* Wire up privacy genesis options into PrivacyParameters

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>

* Refactor private state genesis into it's own class

- pass through in privacyParameters

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>

* Refactor: inject PrivateStateGenesis into PrivacyPrecompiles

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>

* Private Genesis initialisation

- set code, balance and storage from private-genesis.json

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>

* Check on-chain with private genesis in acceptance tests

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>

* Remove unused EthGetCodeCall

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>

* Use a plugin based aproach for privacy genesis

- if the plugin is registered it will be used to apply private genesis
state
- if onchain flexible privacy groups is enable that will be applied
after

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>

* PrivateGenesisAcceptanceTest::createPrivacyGroup can be private

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>

* PrivateStateGenesis add debug logs

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>

* Warn if genesis account allocation is in reserved precompile range

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>

* Add sender into unsignedPrivateMarkerTransaction for plugin to make descision

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>

* Remove locking solution as may not be needed

- if this is required we should first evaluate actual use cases
and test scenarios

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>

* Rename PrivateStateGenesis -> PrivateStateGenesisAllocator

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>

* Tidy up naming for getPrivateStateGenesisAllocator

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>

* Privacy Plugin javadocs

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>
pull/2623/head
Antony Denyer 3 years ago committed by GitHub
parent 047b680ddc
commit 19c34514a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 41
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/privacy/PrivacyNodeFactory.java
  2. 2
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyAcceptanceTestBase.java
  3. 87
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/contract/LoadPrivateSmartContractTransactionWithPrivacyGroupId.java
  4. 13
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/contract/PrivateContractTransactions.java
  5. 80
      acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/TestPrivacyServicePlugin.java
  6. 87
      acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/privacy/TestPrivacyGroupGenesisProvider.java
  7. 62
      acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/privacy/TestPrivacyPluginPayloadProvider.java
  8. 9
      acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/privacy/TestSigningPrivateMarkerTransactionFactory.java
  9. 39
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/PrivDebugGetStateRootOnchainGroupAcceptanceTest.java
  10. 95
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/PrivateGenesisAcceptanceTest.java
  11. 46
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/OnChainPrivacyAcceptanceTest.java
  12. 2
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/OnChainPrivacyAcceptanceTestBase.java
  13. 13
      besu/src/main/java/org/hyperledger/besu/services/PrivacyPluginServiceImpl.java
  14. 42
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/AbstractEeaSendRawTransaction.java
  15. 6
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/PluginEeaSendRawTransaction.java
  16. 10
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/RestrictedOffChainEeaSendRawTransaction.java
  17. 6
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/RestrictedOnChainEeaSendRawTransaction.java
  18. 7
      ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java
  19. 21
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java
  20. 46
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessor.java
  21. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java
  22. 59
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/OnChainPrivacyPrecompiledContract.java
  23. 7
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPluginPrecompiledContract.java
  24. 36
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java
  25. 54
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateGroupRehydrationBlockProcessor.java
  26. 113
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateGenesisAllocator.java
  27. 8
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateRehydration.java
  28. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java
  29. 5
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java
  30. 16
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/OnChainPrivacyPrecompiledContractTest.java
  31. 13
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPluginPrecompiledContractTest.java
  32. 18
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java
  33. 160
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivateStateGenesisAllocatorTest.java
  34. 2
      plugin-api/build.gradle
  35. 27
      plugin-api/src/main/java/org/hyperledger/besu/plugin/data/PrivacyGenesis.java
  36. 66
      plugin-api/src/main/java/org/hyperledger/besu/plugin/data/PrivacyGenesisAccount.java
  37. 2
      plugin-api/src/main/java/org/hyperledger/besu/plugin/data/Quantity.java
  38. 42
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/PrivacyPluginService.java
  39. 11
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/privacy/PrivacyGroupAuthProvider.java
  40. 32
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/privacy/PrivacyGroupGenesisProvider.java
  41. 26
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/privacy/PrivacyPluginPayloadProvider.java
  42. 19
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/privacy/PrivateMarkerTransactionFactory.java

@ -173,6 +173,43 @@ public class PrivacyNodeFactory {
containerNetwork);
}
public PrivacyNode createIbft2NodePrivacyEnabledWithGenesis(
final String name,
final PrivacyAccount privacyAccount,
final boolean minerEnabled,
final EnclaveType enclaveType,
final Optional<Network> containerNetwork,
final boolean isOnchainPrivacyGroupEnabled,
final boolean isMultitenancyEnabled,
final boolean isPrivacyPluginEnabled,
final String unrestrictedPrefix)
throws IOException {
return create(
new PrivacyNodeConfiguration(
isOnchainPrivacyGroupEnabled,
isMultitenancyEnabled,
isPrivacyPluginEnabled,
new BesuNodeConfigurationBuilder()
.name(name)
.miningEnabled()
.jsonRpcConfiguration(node.createJsonRpcWithIbft2EnabledConfig(minerEnabled))
.webSocketConfiguration(node.createWebSocketEnabledConfig())
.devMode(false)
.genesisConfigProvider(genesis::createPrivacyIbft2GenesisConfig)
.keyFilePath(privacyAccount.getPrivateKeyPath())
.enablePrivateTransactions()
.plugins(Collections.singletonList("testPlugins"))
.extraCLIOptions(
List.of(
"--plugin-privacy-service-encryption-prefix=" + unrestrictedPrefix,
"--plugin-privacy-service-genesis-enabled=true"))
.build(),
new EnclaveKeyConfiguration(
privacyAccount.getEnclaveKeyPaths(), privacyAccount.getEnclavePrivateKeyPaths())),
enclaveType,
containerNetwork);
}
public PrivacyNode createQbftNodePrivacyEnabled(
final String name,
final PrivacyAccount privacyAccount,
@ -231,6 +268,8 @@ public class PrivacyNodeFactory {
.jsonRpcEnabled()
.webSocketEnabled()
.enablePrivateTransactions()
.plugins(Collections.singletonList("testPlugins"))
.extraCLIOptions(List.of("--plugin-privacy-service-genesis-enabled=true"))
.keyFilePath(privacyAccount.getPrivateKeyPath())
.build(),
new EnclaveKeyConfiguration(
@ -256,6 +295,8 @@ public class PrivacyNodeFactory {
.jsonRpcEnabled()
.keyFilePath(privacyAccount.getPrivateKeyPath())
.enablePrivateTransactions()
.plugins(Collections.singletonList("testPlugins"))
.extraCLIOptions(List.of("--plugin-privacy-service-genesis-enabled=true"))
.webSocketEnabled()
.build(),
new EnclaveKeyConfiguration(

@ -40,8 +40,6 @@ import org.junit.rules.TemporaryFolder;
public class PrivacyAcceptanceTestBase {
@ClassRule public static final TemporaryFolder privacy = new TemporaryFolder();
protected static final long POW_CHAIN_ID = 1337;
protected final PrivacyTransactions privacyTransactions;
protected final PrivateContractVerifier privateContractVerifier;
protected final PrivateTransactionVerifier privateTransactionVerifier;

@ -0,0 +1,87 @@
/*
* 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.tests.acceptance.dsl.privacy.contract;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
import java.lang.reflect.Method;
import java.math.BigInteger;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.tx.Contract;
import org.web3j.tx.TransactionManager;
import org.web3j.tx.gas.BesuPrivacyGasProvider;
import org.web3j.tx.gas.ContractGasProvider;
import org.web3j.utils.Base64String;
public class LoadPrivateSmartContractTransactionWithPrivacyGroupId<T extends Contract>
implements Transaction<T> {
private static final BesuPrivacyGasProvider GAS_PROVIDER =
new BesuPrivacyGasProvider(BigInteger.valueOf(1000));
private static final Object METHOD_IS_STATIC = null;
private final Class<T> clazz;
private final Credentials senderCredentials;
private final Base64String privateFrom;
private final Base64String privacyGroupId;
private final String contractAddress;
public LoadPrivateSmartContractTransactionWithPrivacyGroupId(
final String contractAddress,
final Class<T> clazz,
final String transactionSigningKey,
final String privateFrom,
final String privacyGroupId) {
this.contractAddress = contractAddress;
this.clazz = clazz;
this.senderCredentials = Credentials.create(transactionSigningKey);
this.privateFrom = Base64String.wrap(privateFrom);
this.privacyGroupId = Base64String.wrap(privacyGroupId);
}
@SuppressWarnings("unchecked")
@Override
public T execute(final NodeRequests node) {
final PrivateTransactionManager privateTransactionManager =
new PrivateTransactionManager.Builder(
node.privacy().getBesuClient(), senderCredentials, privateFrom)
.setPrivacyGroupId(privacyGroupId)
.build();
try {
final Method method =
clazz.getMethod(
"load",
String.class,
Web3j.class,
TransactionManager.class,
ContractGasProvider.class);
return (T)
method.invoke(
METHOD_IS_STATIC,
contractAddress,
node.privacy().getBesuClient(),
privateTransactionManager,
GAS_PROVIDER);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
}

@ -136,7 +136,7 @@ public class PrivateContractTransactions {
contractAddress, clazz, transactionSigningKey, privateFrom, Arrays.asList(privateFor));
}
private <T extends Contract> LoadPrivateSmartContractTransaction<T> loadSmartContract(
public <T extends Contract> LoadPrivateSmartContractTransaction<T> loadSmartContract(
final String contractAddress,
final Class<T> clazz,
final String transactionSigningKey,
@ -146,6 +146,17 @@ public class PrivateContractTransactions {
contractAddress, clazz, transactionSigningKey, privateFrom, privateFor);
}
public <T extends Contract>
LoadPrivateSmartContractTransactionWithPrivacyGroupId<T> loadSmartContractWithPrivacyGroupId(
final String contractAddress,
final Class<T> clazz,
final String transactionSigningKey,
final String privateFrom,
final String privacyGroupId) {
return new LoadPrivateSmartContractTransactionWithPrivacyGroupId<>(
contractAddress, clazz, transactionSigningKey, privateFrom, privacyGroupId);
}
public CallOnChainPermissioningPrivateSmartContractFunction callOnChainPermissioningSmartContract(
final String contractAddress,
final String encodedFunction,

@ -14,24 +14,17 @@
*/
package org.hyperledger.besu.plugins;
import static org.hyperledger.besu.ethereum.privacy.PrivateTransaction.readFrom;
import static org.hyperledger.besu.ethereum.privacy.PrivateTransaction.serialize;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.plugin.BesuContext;
import org.hyperledger.besu.plugin.BesuPlugin;
import org.hyperledger.besu.plugin.data.PrivateTransaction;
import org.hyperledger.besu.plugin.data.Transaction;
import org.hyperledger.besu.plugin.services.PicoCLIOptions;
import org.hyperledger.besu.plugin.services.PrivacyPluginService;
import org.hyperledger.besu.plugin.services.privacy.PrivacyPluginPayloadProvider;
import java.util.Optional;
import org.hyperledger.besu.plugins.privacy.TestPrivacyGroupGenesisProvider;
import org.hyperledger.besu.plugins.privacy.TestPrivacyPluginPayloadProvider;
import org.hyperledger.besu.plugins.privacy.TestSigningPrivateMarkerTransactionFactory;
import com.google.auto.service.AutoService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes;
import picocli.CommandLine.Option;
@AutoService(BesuPlugin.class)
@ -41,6 +34,12 @@ public class TestPrivacyServicePlugin implements BesuPlugin {
PrivacyPluginService pluginService;
BesuContext context;
TestPrivacyPluginPayloadProvider payloadProvider = new TestPrivacyPluginPayloadProvider();
TestPrivacyGroupGenesisProvider privacyGroupGenesisProvider =
new TestPrivacyGroupGenesisProvider();
TestSigningPrivateMarkerTransactionFactory privateMarkerTransactionFactory =
new TestSigningPrivateMarkerTransactionFactory();
@Override
public void register(final BesuContext context) {
this.context = context;
@ -48,39 +47,25 @@ public class TestPrivacyServicePlugin implements BesuPlugin {
context.getService(PicoCLIOptions.class).get().addPicoCLIOptions("privacy-service", this);
pluginService = context.getService(PrivacyPluginService.class).get();
pluginService.setPayloadProvider(
new PrivacyPluginPayloadProvider() {
@Override
public Bytes generateMarkerPayload(
final PrivateTransaction privateTransaction, final String privacyUserId) {
return Bytes.wrap(Bytes.fromHexString(prefix), serialize(privateTransaction).encoded());
}
@Override
public Optional<PrivateTransaction> getPrivateTransactionFromPayload(
final Transaction transaction) {
final Bytes prefixBytes = Bytes.fromHexString(prefix);
if (transaction.getPayload().slice(0, prefixBytes.size()).equals(prefixBytes)) {
LOG.info("processing payload for" + prefix);
final BytesValueRLPInput bytesValueRLPInput =
new BytesValueRLPInput(
transaction.getPayload().slice(prefixBytes.size()).copy(), false);
return Optional.of(readFrom(bytesValueRLPInput));
} else {
LOG.info("Can not process payload for" + prefix);
return Optional.empty();
}
}
});
pluginService.setPayloadProvider(payloadProvider);
pluginService.setPrivacyGroupGenesisProvider(privacyGroupGenesisProvider);
LOG.info("Registering Plugins with options " + this);
}
@Override
public void start() {
LOG.info("Start Plugins with options " + this);
payloadProvider.setPluginPayloadPrefix(prefix);
if (genesisEnabled) {
privacyGroupGenesisProvider.setGenesisEnabled();
}
if (signingEnabled) {
pluginService.setPrivateMarkerTransactionFactory(
new TestSigningPrivateMarkerTransactionFactory(signingKey));
pluginService.setPrivateMarkerTransactionFactory(privateMarkerTransactionFactory);
privateMarkerTransactionFactory.setSigningKeyEnbaled(signingKey);
}
}
@ -90,9 +75,28 @@ public class TestPrivacyServicePlugin implements BesuPlugin {
@Option(names = "--plugin-privacy-service-encryption-prefix")
String prefix;
@Option(names = "--plugin-privacy-service-genesis-enabled")
boolean genesisEnabled = false;
@Option(names = "--plugin-privacy-service-signing-enabled")
boolean signingEnabled = false;
@Option(names = "--plugin-privacy-service-signing-key")
String signingKey;
@Override
public String toString() {
return "TestPrivacyServicePlugin{"
+ "prefix='"
+ prefix
+ '\''
+ ", signingEnabled="
+ signingEnabled
+ ", genesisEnabled="
+ genesisEnabled
+ ", signingKey='"
+ signingKey
+ '\''
+ '}';
}
}

@ -0,0 +1,87 @@
/*
* 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.plugins.privacy;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.plugin.data.Address;
import org.hyperledger.besu.plugin.data.PrivacyGenesis;
import org.hyperledger.besu.plugin.data.PrivacyGenesisAccount;
import org.hyperledger.besu.plugin.data.Quantity;
import org.hyperledger.besu.plugin.services.privacy.PrivacyGroupGenesisProvider;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
public class TestPrivacyGroupGenesisProvider implements PrivacyGroupGenesisProvider {
private boolean genesisEnabled = false;
public void setGenesisEnabled() {
this.genesisEnabled = true;
}
@Override
public PrivacyGenesis getPrivacyGenesis(final Bytes privacyGroupId, final long blockNumber) {
if (!genesisEnabled) return Collections::emptyList;
return () ->
List.of(
new PrivacyGenesisAccount() {
@Override
public Address getAddress() {
return org.hyperledger.besu.ethereum.core.Address.fromHexString(
"0x1000000000000000000000000000000000000001");
}
@Override
public Map<UInt256, UInt256> getStorage() {
return Collections.emptyMap();
}
@Override
public int getVersion() {
return 0;
}
@Override
public Long getNonce() {
return 0L;
}
@Override
public Quantity getBalance() {
return Wei.of(1000);
}
// The code in the genesis file needs to be the deployed contract code, not the code
// to deploy
// the contract
// you can generate it using the solc --bin-runtime flag
// cd ./acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/
// docker run -v $PWD:/sources ethereum/solc:0.5.0 -o /sources/output --bin-runtime
// /sources/EventEmitter.sol --overwrite
@Override
public Bytes getCode() {
return Bytes.fromHexString(
"0x608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633fa4f2451461005c5780636057361d1461008757806367e404ce146100c2575b600080fd5b34801561006857600080fd5b50610071610119565b6040518082815260200191505060405180910390f35b34801561009357600080fd5b506100c0600480360360208110156100aa57600080fd5b8101908080359060200190929190505050610123565b005b3480156100ce57600080fd5b506100d76101d9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6000600254905090565b7fc9db20adedc6cf2b5d25252b101ab03e124902a73fcb12b753f3d1aaa2d8f9f53382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a18060028190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690509056fea165627a7a72305820e74360c3d08936cb1747ad641729261ff5e83b6fc0d303d136e171f15f07d7740029");
}
});
}
}

@ -0,0 +1,62 @@
/*
* 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.plugins.privacy;
import static org.hyperledger.besu.ethereum.privacy.PrivateTransaction.readFrom;
import static org.hyperledger.besu.ethereum.privacy.PrivateTransaction.serialize;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.plugin.data.PrivateTransaction;
import org.hyperledger.besu.plugin.data.Transaction;
import org.hyperledger.besu.plugin.services.privacy.PrivacyPluginPayloadProvider;
import java.util.Optional;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes;
public class TestPrivacyPluginPayloadProvider implements PrivacyPluginPayloadProvider {
private static final Logger LOG = LogManager.getLogger();
private String prefix;
public void setPluginPayloadPrefix(final String prefix) {
this.prefix = prefix;
}
@Override
public Bytes generateMarkerPayload(
final PrivateTransaction privateTransaction, final String privacyUserId) {
return Bytes.wrap(Bytes.fromHexString(prefix), serialize(privateTransaction).encoded());
}
@Override
public Optional<PrivateTransaction> getPrivateTransactionFromPayload(
final Transaction transaction) {
final Bytes prefixBytes = Bytes.fromHexString(prefix);
if (transaction.getPayload().slice(0, prefixBytes.size()).equals(prefixBytes)) {
LOG.info("processing payload for " + prefix);
final BytesValueRLPInput bytesValueRLPInput =
new BytesValueRLPInput(transaction.getPayload().slice(prefixBytes.size()).copy(), false);
return Optional.of(readFrom(bytesValueRLPInput));
} else {
LOG.info("Can not process payload for " + prefix);
return Optional.empty();
}
}
}

@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.plugins;
package org.hyperledger.besu.plugins.privacy;
import static org.apache.logging.log4j.LogManager.getLogger;
import static org.hyperledger.besu.ethereum.core.Address.extract;
@ -39,11 +39,10 @@ public class TestSigningPrivateMarkerTransactionFactory implements PrivateMarker
private static final Logger LOG = getLogger();
final KeyPair aliceFixedSigningKey;
final Address sender;
KeyPair aliceFixedSigningKey;
Address sender;
public TestSigningPrivateMarkerTransactionFactory(
final String privateMarkerTransactionSigningKey) {
public void setSigningKeyEnbaled(final String privateMarkerTransactionSigningKey) {
final SignatureAlgorithm algorithm = SignatureAlgorithmFactory.getInstance();
final SECPPrivateKey privateKey =
algorithm.createPrivateKey(Bytes32.fromHexString(privateMarkerTransactionSigningKey));

@ -20,10 +20,12 @@ import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyNode;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.account.PrivacyAccountResolver;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyRequestFactory;
import org.hyperledger.besu.tests.web3j.generated.EventEmitter;
import org.hyperledger.besu.tests.web3j.privacy.OnChainPrivacyAcceptanceTestBase;
import org.hyperledger.enclave.testutil.EnclaveType;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collection;
@ -76,6 +78,7 @@ public class PrivDebugGetStateRootOnchainGroupAcceptanceTest
false,
enclaveType,
Optional.of(containerNetwork));
privacyCluster.start(aliceNode, bobNode);
}
@ -139,4 +142,40 @@ public class PrivDebugGetStateRootOnchainGroupAcceptanceTest
assertThat(aliceResultLatest).isEqualTo(bobResultLatest);
assertThat(aliceResult1).isNotEqualTo(aliceResultLatest);
}
@Test
public void canInteractWithPrivateGenesisPreCompile() throws Exception {
final String privacyGroupId = createOnChainPrivacyGroup(aliceNode, bobNode);
final EventEmitter eventEmitter =
aliceNode.execute(
privateContractTransactions.loadSmartContractWithPrivacyGroupId(
"0x1000000000000000000000000000000000000001",
EventEmitter.class,
aliceNode.getTransactionSigningKey(),
aliceNode.getEnclaveKey(),
privacyGroupId));
eventEmitter.store(BigInteger.valueOf(42)).send();
final String aliceResponse =
aliceNode
.execute(
privacyTransactions.privCall(
privacyGroupId, eventEmitter, eventEmitter.value().encodeFunctionCall()))
.getValue();
assertThat(new BigInteger(aliceResponse.substring(2), 16))
.isEqualByComparingTo(BigInteger.valueOf(42));
final String bobResponse =
bobNode
.execute(
privacyTransactions.privCall(
privacyGroupId, eventEmitter, eventEmitter.value().encodeFunctionCall()))
.getValue();
assertThat(new BigInteger(bobResponse.substring(2), 16))
.isEqualByComparingTo(BigInteger.valueOf(42));
}
}

@ -0,0 +1,95 @@
/*
* 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.tests.acceptance.privacy;
import static org.assertj.core.api.Assertions.assertThat;
import static org.web3j.utils.Restriction.RESTRICTED;
import static org.web3j.utils.Restriction.UNRESTRICTED;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.ParameterizedEnclaveTestBase;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyNode;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.account.PrivacyAccountResolver;
import org.hyperledger.besu.tests.web3j.generated.EventEmitter;
import org.hyperledger.enclave.testutil.EnclaveType;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Optional;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.web3j.protocol.core.methods.response.EthCall;
import org.web3j.utils.Restriction;
@RunWith(Parameterized.class)
public class PrivateGenesisAcceptanceTest extends ParameterizedEnclaveTestBase {
private final PrivacyNode alice;
public PrivateGenesisAcceptanceTest(final Restriction restriction, final EnclaveType enclaveType)
throws IOException {
super(restriction, enclaveType);
alice =
privacyBesu.createIbft2NodePrivacyEnabledWithGenesis(
"node1",
PrivacyAccountResolver.ALICE,
true,
enclaveType,
Optional.empty(),
false,
false,
restriction == UNRESTRICTED,
"AA");
privacyCluster.start(alice);
}
@Test
public void canInteractWithPrivateGenesisPreCompile() throws Exception {
final String privacyGroupId = createPrivacyGroup();
final EventEmitter eventEmitter =
alice.execute(
privateContractTransactions.loadSmartContractWithPrivacyGroupId(
"0x1000000000000000000000000000000000000001",
EventEmitter.class,
alice.getTransactionSigningKey(),
alice.getEnclaveKey(),
privacyGroupId));
eventEmitter.store(BigInteger.valueOf(42)).send();
final EthCall response =
alice.execute(
privacyTransactions.privCall(
privacyGroupId, eventEmitter, eventEmitter.value().encodeFunctionCall()));
final String value = response.getValue();
assertThat(new BigInteger(value.substring(2), 16)).isEqualByComparingTo(BigInteger.valueOf(42));
}
private String createPrivacyGroup() {
if (restriction == RESTRICTED) {
return alice.execute(privacyTransactions.createPrivacyGroup("name", "description", alice));
} else if (restriction == UNRESTRICTED) {
return "gsvwYfGPurL7wgXKmgFtCamXarAl9fA5jaSXi8TLpJw=";
} else {
throw new RuntimeException("Do not know how to handle " + restriction);
}
}
}

@ -62,8 +62,6 @@ public class OnChainPrivacyAcceptanceTest extends OnChainPrivacyAcceptanceTestBa
.collect(Collectors.toList());
}
protected static final long POW_CHAIN_ID = 1337;
private PrivacyNode alice;
private PrivacyNode bob;
private PrivacyNode charlie;
@ -151,6 +149,50 @@ public class OnChainPrivacyAcceptanceTest extends OnChainPrivacyAcceptanceTestBa
.hasMessageContaining("Onchain Privacy group does not exist.");
}
@Test
public void canInteractWithPrivateGenesisPreCompile() throws Exception {
final String privacyGroupId = createOnChainPrivacyGroup(alice, bob);
final EventEmitter eventEmitter =
alice.execute(
privateContractTransactions.loadSmartContractWithPrivacyGroupId(
"0x1000000000000000000000000000000000000001",
EventEmitter.class,
alice.getTransactionSigningKey(),
alice.getEnclaveKey(),
privacyGroupId));
eventEmitter.store(BigInteger.valueOf(42)).send();
final String aliceResponse =
alice
.execute(
privacyTransactions.privCall(
privacyGroupId, eventEmitter, eventEmitter.value().encodeFunctionCall()))
.getValue();
assertThat(new BigInteger(aliceResponse.substring(2), 16))
.isEqualByComparingTo(BigInteger.valueOf(42));
final String bobResponse =
bob.execute(
privacyTransactions.privCall(
privacyGroupId, eventEmitter, eventEmitter.value().encodeFunctionCall()))
.getValue();
assertThat(new BigInteger(bobResponse.substring(2), 16))
.isEqualByComparingTo(BigInteger.valueOf(42));
final String charlieResponse =
charlie
.execute(
privacyTransactions.privCall(
privacyGroupId, eventEmitter, eventEmitter.value().encodeFunctionCall()))
.getValue();
assertThat(charlieResponse).isEqualTo("0x");
}
@Test
public void memberCanBeAddedAfterBeingRemoved() {
final String privacyGroupId = createOnChainPrivacyGroup(alice);

@ -46,7 +46,7 @@ public class OnChainPrivacyAcceptanceTestBase extends PrivacyAcceptanceTestBase
}
/**
* Crete an onchain privacy group. The privacy group id will be randomly generated.
* Create an onchain privacy group. The privacy group id will be randomly generated.
*
* <p>This method also checks that each node member has successfully processed the transaction and
* has the expected list of member for the group.

@ -16,6 +16,7 @@ package org.hyperledger.besu.services;
import org.hyperledger.besu.plugin.services.PrivacyPluginService;
import org.hyperledger.besu.plugin.services.privacy.PrivacyGroupAuthProvider;
import org.hyperledger.besu.plugin.services.privacy.PrivacyGroupGenesisProvider;
import org.hyperledger.besu.plugin.services.privacy.PrivacyPluginPayloadProvider;
import org.hyperledger.besu.plugin.services.privacy.PrivateMarkerTransactionFactory;
@ -30,6 +31,7 @@ public class PrivacyPluginServiceImpl implements PrivacyPluginService {
private PrivacyGroupAuthProvider privacyGroupAuthProvider =
(privacyGroupId, privacyUserId, blockNumber) -> true;
private PrivacyGroupGenesisProvider privacyGroupGenesisProvider;
@Override
public void setPayloadProvider(final PrivacyPluginPayloadProvider privacyPluginPayloadProvider) {
@ -65,4 +67,15 @@ public class PrivacyPluginServiceImpl implements PrivacyPluginService {
final PrivateMarkerTransactionFactory privateMarkerTransactionFactory) {
this.privateMarkerTransactionFactory = privateMarkerTransactionFactory;
}
@Override
public void setPrivacyGroupGenesisProvider(
final PrivacyGroupGenesisProvider privacyGroupGenesisProvider) {
this.privacyGroupGenesisProvider = privacyGroupGenesisProvider;
}
@Override
public PrivacyGroupGenesisProvider getPrivacyGroupGenesisProvider() {
return privacyGroupGenesisProvider;
}
}

@ -41,9 +41,7 @@ import org.hyperledger.besu.plugin.data.TransactionType;
import org.hyperledger.besu.plugin.services.privacy.PrivateMarkerTransactionFactory;
import java.util.Optional;
import java.util.concurrent.locks.Lock;
import com.google.common.util.concurrent.Striped;
import io.vertx.ext.auth.User;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes;
@ -54,9 +52,6 @@ public abstract class AbstractEeaSendRawTransaction implements JsonRpcMethod {
private final PrivacyIdProvider privacyIdProvider;
private final PrivateMarkerTransactionFactory privateMarkerTransactionFactory;
private final NonceProvider publicNonceProvider;
private static final int MAX_CONCURRENT_PMT_SIGNATURE_REQUESTS = 10;
private final Striped<Lock> stripedLock =
Striped.lazyWeakLock(MAX_CONCURRENT_PMT_SIGNATURE_REQUESTS);
protected AbstractEeaSendRawTransaction(
final TransactionPool transactionPool,
@ -96,29 +91,14 @@ public abstract class AbstractEeaSendRawTransaction implements JsonRpcMethod {
privateMarkerTransactionFactory.getSender(
privateTransaction, privacyIdProvider.getPrivacyUserId(user));
// We lock by sender address so that we can send multiple requests from the same address
// the request will be blocked until the nonce for that sender has been calculated and the
// transaction
// has completed submission to the transaction pool
final Transaction privateMarkerTransaction =
createPrivateMarkerTransaction(Address.fromPlugin(sender), privateTransaction, user);
final Lock lock = stripedLock.get(sender.toShortHexString());
lock.lock();
try {
final Transaction privateMarkerTransaction =
createPrivateMarkerTransaction(privateTransaction, user);
LOG.error("CHEESE");
LOG.error(privateMarkerTransaction.getHash());
return transactionPool
.addLocalTransaction(privateMarkerTransaction)
.either(
() -> new JsonRpcSuccessResponse(id, privateMarkerTransaction.getHash().toString()),
errorReason -> getJsonRpcErrorResponse(id, errorReason));
} finally {
lock.unlock();
}
return transactionPool
.addLocalTransaction(privateMarkerTransaction)
.either(
() -> new JsonRpcSuccessResponse(id, privateMarkerTransaction.getHash().toString()),
errorReason -> getJsonRpcErrorResponse(id, errorReason));
} catch (final JsonRpcErrorResponseException e) {
return new JsonRpcErrorResponse(id, e.getJsonRpcError());
} catch (final IllegalArgumentException | RLPException e) {
@ -141,23 +121,21 @@ public abstract class AbstractEeaSendRawTransaction implements JsonRpcMethod {
final PrivateTransaction privateTransaction, final Optional<User> user);
protected abstract Transaction createPrivateMarkerTransaction(
final PrivateTransaction privateTransaction, final Optional<User> user);
final Address sender, final PrivateTransaction privateTransaction, final Optional<User> user);
protected Transaction createPrivateMarkerTransaction(
final Address sender,
final Address privacyPrecompileAddress,
final String pmtPayload,
final PrivateTransaction privateTransaction,
final String privacyUserId) {
final Address sender =
Address.fromPlugin(
privateMarkerTransactionFactory.getSender(privateTransaction, privacyUserId));
final long nonce = publicNonceProvider.getNonce(sender);
final Transaction unsignedPrivateMarkerTransaction =
new Transaction.Builder()
.type(TransactionType.FRONTIER)
.sender(sender)
.nonce(nonce)
.gasPrice(privateTransaction.getGasPrice())
.gasLimit(getGasLimit(privateTransaction, pmtPayload))

@ -60,7 +60,9 @@ public class PluginEeaSendRawTransaction extends AbstractEeaSendRawTransaction {
@Override
protected Transaction createPrivateMarkerTransaction(
final PrivateTransaction privateTransaction, final Optional<User> user) {
final Address sender,
final PrivateTransaction privateTransaction,
final Optional<User> user) {
final String privacyUserId = privacyIdProvider.getPrivacyUserId(user);
@ -69,7 +71,7 @@ public class PluginEeaSendRawTransaction extends AbstractEeaSendRawTransaction {
privateTransaction, privacyUserId, Optional.empty());
return createPrivateMarkerTransaction(
Address.PLUGIN_PRIVACY, payloadFromPlugin, privateTransaction, privacyUserId);
sender, Address.PLUGIN_PRIVACY, payloadFromPlugin, privateTransaction, privacyUserId);
}
@Override

@ -71,7 +71,9 @@ public class RestrictedOffChainEeaSendRawTransaction extends AbstractEeaSendRawT
@Override
protected Transaction createPrivateMarkerTransaction(
final PrivateTransaction privateTransaction, final Optional<User> user) {
final Address sender,
final PrivateTransaction privateTransaction,
final Optional<User> user) {
final String privacyUserId = privacyIdProvider.getPrivacyUserId(user);
@ -84,7 +86,11 @@ public class RestrictedOffChainEeaSendRawTransaction extends AbstractEeaSendRawT
privateTransaction, privacyUserId, maybePrivacyGroup);
return createPrivateMarkerTransaction(
Address.DEFAULT_PRIVACY, privateTransactionLookupId, privateTransaction, privacyUserId);
sender,
Address.DEFAULT_PRIVACY,
privateTransactionLookupId,
privateTransaction,
privacyUserId);
}
@Override

@ -65,7 +65,9 @@ public class RestrictedOnChainEeaSendRawTransaction extends AbstractEeaSendRawTr
@Override
protected Transaction createPrivateMarkerTransaction(
final PrivateTransaction privateTransaction, final Optional<User> user) {
final Address sender,
final PrivateTransaction privateTransaction,
final Optional<User> user) {
if (privateTransaction.getPrivacyGroupId().isEmpty()) {
throw new JsonRpcErrorResponseException(JsonRpcError.ONCHAIN_PRIVACY_GROUP_ID_NOT_AVAILABLE);
}
@ -96,7 +98,7 @@ public class RestrictedOnChainEeaSendRawTransaction extends AbstractEeaSendRawTr
buildCompoundLookupId(privateTransactionLookupId, addPayloadPrivateTransactionLookupId);
return createPrivateMarkerTransaction(
Address.ONCHAIN_PRIVACY, pmtPayload, privateTransaction, privacyUserId);
sender, Address.ONCHAIN_PRIVACY, pmtPayload, privateTransaction, privacyUserId);
}
@Override

@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.WorldUpdater;
import org.hyperledger.besu.ethereum.mainnet.SpuriousDragonGasCalculator;
import org.hyperledger.besu.ethereum.privacy.PrivateStateGenesisAllocator;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
@ -50,6 +51,7 @@ import org.hyperledger.enclave.testutil.EnclaveKeyConfiguration;
import org.hyperledger.enclave.testutil.OrionTestHarness;
import org.hyperledger.enclave.testutil.OrionTestHarnessFactory;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@ -192,7 +194,10 @@ public class PrivacyPrecompiledContractIntegrationTest {
new SpuriousDragonGasCalculator(),
enclave,
worldStateArchive,
new PrivateStateRootResolver(privateStateStorage));
new PrivateStateRootResolver(privateStateStorage),
new PrivateStateGenesisAllocator(
false, (privacyGroupId, blockNumber) -> Collections::emptyList),
"IntegrationTest");
privacyPrecompiledContract.setPrivateTransactionProcessor(mockPrivateTxProcessor());

@ -20,6 +20,7 @@ import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.KeyPairUtil;
import org.hyperledger.besu.enclave.Enclave;
import org.hyperledger.besu.enclave.EnclaveFactory;
import org.hyperledger.besu.ethereum.privacy.PrivateStateGenesisAllocator;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.PrivateWorldStateReader;
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyStorageProvider;
@ -29,12 +30,14 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.plugin.services.PrivacyPluginService;
import org.hyperledger.besu.plugin.services.privacy.PrivacyGroupGenesisProvider;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
import java.util.Base64;
import java.util.Collections;
import java.util.Optional;
import com.google.common.annotations.VisibleForTesting;
@ -204,6 +207,24 @@ public class PrivacyParameters {
return privacyPluginService;
}
public PrivateStateGenesisAllocator getPrivateStateGenesisAllocator() {
// Note: the order of plugin registration may cause issues here.
// This is why it's instantiated on get. It's needed in the privacy pre-compile constructors
// but privacy parameters is built before the plugin has had a chance to register a provider
// and have cli options instantiated
return new PrivateStateGenesisAllocator(
onchainPrivacyGroupsEnabled, createPrivateGenesisProvider());
}
private PrivacyGroupGenesisProvider createPrivateGenesisProvider() {
if (privacyPluginService != null
&& privacyPluginService.getPrivacyGroupGenesisProvider() != null) {
return privacyPluginService.getPrivacyGroupGenesisProvider();
} else {
return (privacyGroupId, blockNumber) -> Collections::emptyList;
}
}
@Override
public String toString() {
return "PrivacyParameters{"

@ -23,6 +23,7 @@ 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.Transaction;
import org.hyperledger.besu.ethereum.privacy.PrivateStateGenesisAllocator;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRehydration;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionWithMetadata;
@ -53,6 +54,7 @@ public class PrivacyBlockProcessor implements BlockProcessor {
private final PrivateStateStorage privateStateStorage;
private final WorldStateArchive privateWorldStateArchive;
private final PrivateStateRootResolver privateStateRootResolver;
private final PrivateStateGenesisAllocator privateStateGenesisAllocator;
private WorldStateArchive publicWorldStateArchive;
public PrivacyBlockProcessor(
@ -61,13 +63,15 @@ public class PrivacyBlockProcessor implements BlockProcessor {
final Enclave enclave,
final PrivateStateStorage privateStateStorage,
final WorldStateArchive privateWorldStateArchive,
final PrivateStateRootResolver privateStateRootResolver) {
final PrivateStateRootResolver privateStateRootResolver,
final PrivateStateGenesisAllocator privateStateGenesisAllocator) {
this.blockProcessor = blockProcessor;
this.protocolSchedule = protocolSchedule;
this.enclave = enclave;
this.privateStateStorage = privateStateStorage;
this.privateWorldStateArchive = privateWorldStateArchive;
this.privateStateRootResolver = privateStateRootResolver;
this.privateStateGenesisAllocator = privateStateGenesisAllocator;
}
public void setPublicWorldStateArchive(final WorldStateArchive publicWorldStateArchive) {
@ -104,16 +108,14 @@ public class PrivacyBlockProcessor implements BlockProcessor {
final BlockHeader blockHeader,
final List<Transaction> transactions) {
transactions.stream()
.filter(
t ->
t.getTo().isPresent()
&& t.getTo().equals(Optional.of(Address.ONCHAIN_PRIVACY))
&& t.getPayload().size() == 64)
.filter(this::onChainAddToGroupPrivateMarkerTransactions)
.forEach(
t -> {
final Bytes32 addKey = Bytes32.wrap(t.getPayload().slice(32, 32));
pmt -> {
final Bytes32 privateTransactionsLookupId =
Bytes32.wrap(pmt.getPayload().slice(32, 32));
try {
final ReceiveResponse receiveResponse = enclave.receive(addKey.toBase64String());
final ReceiveResponse receiveResponse =
enclave.receive(privateTransactionsLookupId.toBase64String());
final List<PrivateTransactionWithMetadata> privateTransactionWithMetadataList =
PrivateTransactionWithMetadata.readListFromPayload(
Bytes.wrap(Base64.getDecoder().decode(receiveResponse.getPayload())));
@ -125,15 +127,15 @@ public class PrivacyBlockProcessor implements BlockProcessor {
.getPrivacyGroupId()
.get());
final List<PrivateTransactionWithMetadata> actualList =
createActualList(
final List<PrivateTransactionWithMetadata> actualListToRehydrate =
transactionsInGroupThatNeedToBeApplied(
blockHeader, privateTransactionWithMetadataList, privacyGroupId);
if (actualList.size() > 0) {
if (actualListToRehydrate.size() > 0) {
LOG.debug(
"Rehydrating privacy group {}, number of transactions to be rehydrated is {} out of a total number of {} transactions.",
privacyGroupId.toString(),
actualList.size(),
actualListToRehydrate.size(),
privateTransactionWithMetadataList.size());
final PrivateStateRehydration privateStateRehydration =
new PrivateStateRehydration(
@ -142,9 +144,13 @@ public class PrivacyBlockProcessor implements BlockProcessor {
protocolSchedule,
publicWorldStateArchive,
privateWorldStateArchive,
privateStateRootResolver);
privateStateRehydration.rehydrate(actualList);
privateStateStorage.updater().putAddDataKey(privacyGroupId, addKey).commit();
privateStateRootResolver,
privateStateGenesisAllocator);
privateStateRehydration.rehydrate(actualListToRehydrate);
privateStateStorage
.updater()
.putAddDataKey(privacyGroupId, privateTransactionsLookupId)
.commit();
}
} catch (final EnclaveClientException e) {
// we were not being added because we have not found the add blob
@ -152,7 +158,13 @@ public class PrivacyBlockProcessor implements BlockProcessor {
});
}
private List<PrivateTransactionWithMetadata> createActualList(
private boolean onChainAddToGroupPrivateMarkerTransactions(final Transaction t) {
return t.getTo().isPresent()
&& t.getTo().equals(Optional.of(Address.ONCHAIN_PRIVACY))
&& t.getPayload().size() == 64;
}
private List<PrivateTransactionWithMetadata> transactionsInGroupThatNeedToBeApplied(
final BlockHeader blockHeader,
final List<PrivateTransactionWithMetadata> privateTransactionWithMetadataList,
final Bytes32 privacyGroupId) {

@ -341,7 +341,8 @@ public class ProtocolSpecBuilder {
privacyParameters.getEnclave(),
privacyParameters.getPrivateStateStorage(),
privacyParameters.getPrivateWorldStateArchive(),
privacyParameters.getPrivateStateRootResolver());
privacyParameters.getPrivateStateRootResolver(),
privacyParameters.getPrivateStateGenesisAllocator());
}
final BlockValidator blockValidator =

@ -14,8 +14,6 @@
*/
package org.hyperledger.besu.ethereum.mainnet.precompiles.privacy;
import static org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver.EMPTY_ROOT_HASH;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
@ -24,14 +22,13 @@ import org.hyperledger.besu.enclave.EnclaveClientException;
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.EvmAccount;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.MutableAccount;
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.PrivateStateGenesisAllocator;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionEvent;
@ -88,18 +85,25 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
final GasCalculator gasCalculator,
final Enclave enclave,
final WorldStateArchive worldStateArchive,
final PrivateStateRootResolver privateStateRootResolver) {
super(gasCalculator, enclave, worldStateArchive, privateStateRootResolver, "OnChainPrivacy");
final PrivateStateRootResolver privateStateRootResolver,
final PrivateStateGenesisAllocator privateStateGenesisAllocator) {
super(
gasCalculator,
enclave,
worldStateArchive,
privateStateRootResolver,
privateStateGenesisAllocator,
"OnChainPrivacy");
}
public OnChainPrivacyPrecompiledContract(
final GasCalculator gasCalculator, final PrivacyParameters privacyParameters) {
super(
this(
gasCalculator,
privacyParameters.getEnclave(),
privacyParameters.getPrivateWorldStateArchive(),
privacyParameters.getPrivateStateRootResolver(),
"OnChainPrivacy");
privacyParameters.getPrivateStateGenesisAllocator());
}
public long addPrivateTransactionObserver(final PrivateTransactionObserver observer) {
@ -162,12 +166,16 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
final WorldUpdater privateWorldStateUpdater = disposablePrivateState.updater();
maybeApplyGenesisToPrivateWorldState(
lastRootHash,
disposablePrivateState,
privateWorldStateUpdater,
privacyGroupId,
currentBlockHeader.getNumber());
final WorldUpdater publicWorldState = messageFrame.getWorldState();
final Blockchain blockchain = messageFrame.getBlockchain();
maybeInjectDefaultManagementAndProxy(
lastRootHash, disposablePrivateState, privateWorldStateUpdater);
if (!canExecute(
messageFrame,
currentBlockHeader,
@ -425,35 +433,6 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
privacyGroupId);
}
protected void maybeInjectDefaultManagementAndProxy(
final Hash lastRootHash,
final MutableWorldState disposablePrivateState,
final WorldUpdater privateWorldStateUpdater) {
if (lastRootHash.equals(EMPTY_ROOT_HASH)) {
// inject management
final EvmAccount managementPrecompile =
privateWorldStateUpdater.createAccount(Address.DEFAULT_ONCHAIN_PRIVACY_MANAGEMENT);
final MutableAccount mutableManagementPrecompiled = managementPrecompile.getMutable();
// this is the code for the simple management contract
mutableManagementPrecompiled.setCode(
OnChainGroupManagement.DEFAULT_GROUP_MANAGEMENT_RUNTIME_BYTECODE);
// inject proxy
final EvmAccount proxyPrecompile =
privateWorldStateUpdater.createAccount(Address.ONCHAIN_PRIVACY_PROXY);
final MutableAccount mutableProxyPrecompiled = proxyPrecompile.getMutable();
// this is the code for the proxy contract
mutableProxyPrecompiled.setCode(OnChainGroupManagement.PROXY_RUNTIME_BYTECODE);
// manually set the management contract address so the proxy can trust it
mutableProxyPrecompiled.setStorageValue(
UInt256.ZERO,
UInt256.fromBytes(Bytes32.leftPad(Address.DEFAULT_ONCHAIN_PRIVACY_MANAGEMENT)));
privateWorldStateUpdater.commit();
disposablePrivateState.persist(null);
}
}
protected boolean onChainPrivacyGroupVersionMatches(
final MessageFrame messageFrame,
final ProcessableBlockHeader currentBlockHeader,

@ -79,6 +79,13 @@ public class PrivacyPluginPrecompiledContract extends PrivacyPrecompiledContract
final WorldUpdater privateWorldStateUpdater = disposablePrivateState.updater();
maybeApplyGenesisToPrivateWorldState(
lastRootHash,
disposablePrivateState,
privateWorldStateUpdater,
privacyGroupId,
messageFrame.getBlockHeader().getNumber());
final TransactionProcessingResult result =
processPrivateTransaction(
messageFrame, privateTransaction, privacyGroupId, privateWorldStateUpdater);

@ -14,6 +14,8 @@
*/
package org.hyperledger.besu.ethereum.mainnet.precompiles.privacy;
import static org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver.EMPTY_ROOT_HASH;
import org.hyperledger.besu.enclave.Enclave;
import org.hyperledger.besu.enclave.EnclaveClientException;
import org.hyperledger.besu.enclave.EnclaveIOException;
@ -27,6 +29,7 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.WorldUpdater;
import org.hyperledger.besu.ethereum.mainnet.AbstractPrecompiledContract;
import org.hyperledger.besu.ethereum.privacy.PrivateStateGenesisAllocator;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
@ -42,7 +45,6 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import java.util.Base64;
import com.google.common.annotations.VisibleForTesting;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes;
@ -52,6 +54,7 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
private final Enclave enclave;
final WorldStateArchive privateWorldStateArchive;
final PrivateStateRootResolver privateStateRootResolver;
private final PrivateStateGenesisAllocator privateStateGenesisAllocator;
PrivateTransactionProcessor privateTransactionProcessor;
private static final Logger LOG = LogManager.getLogger();
@ -65,28 +68,22 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
privacyParameters.getEnclave(),
privacyParameters.getPrivateWorldStateArchive(),
privacyParameters.getPrivateStateRootResolver(),
privacyParameters.getPrivateStateGenesisAllocator(),
name);
}
@VisibleForTesting
PrivacyPrecompiledContract(
final GasCalculator gasCalculator,
final Enclave enclave,
final WorldStateArchive worldStateArchive,
final PrivateStateRootResolver privateStateRootResolver) {
this(gasCalculator, enclave, worldStateArchive, privateStateRootResolver, "Privacy");
}
protected PrivacyPrecompiledContract(
final GasCalculator gasCalculator,
final Enclave enclave,
final WorldStateArchive worldStateArchive,
final PrivateStateRootResolver privateStateRootResolver,
final PrivateStateGenesisAllocator privateStateGenesisAllocator,
final String name) {
super(name, gasCalculator);
this.enclave = enclave;
this.privateWorldStateArchive = worldStateArchive;
this.privateStateRootResolver = privateStateRootResolver;
this.privateStateGenesisAllocator = privateStateGenesisAllocator;
}
public void setPrivateTransactionProcessor(
@ -161,6 +158,13 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
final WorldUpdater privateWorldStateUpdater = disposablePrivateState.updater();
maybeApplyGenesisToPrivateWorldState(
lastRootHash,
disposablePrivateState,
privateWorldStateUpdater,
privacyGroupId,
messageFrame.getBlockHeader().getNumber());
final TransactionProcessingResult result =
processPrivateTransaction(
messageFrame, privateTransaction, privacyGroupId, privateWorldStateUpdater);
@ -188,6 +192,18 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
return result.getOutput();
}
protected void maybeApplyGenesisToPrivateWorldState(
final Hash lastRootHash,
final MutableWorldState disposablePrivateState,
final WorldUpdater privateWorldStateUpdater,
final Bytes32 privacyGroupId,
final long blockNumber) {
if (lastRootHash.equals(EMPTY_ROOT_HASH)) {
this.privateStateGenesisAllocator.applyGenesisToPrivateWorldState(
disposablePrivateState, privateWorldStateUpdater, privacyGroupId, blockNumber);
}
}
void storePrivateMetadata(
final Hash commitmentHash,
final Bytes32 privacyGroupId,

@ -20,7 +20,6 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.EvmAccount;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.MutableAccount;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
@ -33,7 +32,6 @@ import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.MiningBeneficiaryCalculator;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateTransactionMetadata;
@ -49,7 +47,6 @@ import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
public class PrivateGroupRehydrationBlockProcessor {
@ -62,6 +59,7 @@ public class PrivateGroupRehydrationBlockProcessor {
private final AbstractBlockProcessor.TransactionReceiptFactory transactionReceiptFactory;
final Wei blockReward;
private final boolean skipZeroBlockRewards;
private final PrivateStateGenesisAllocator privateStateGenesisAllocator;
private final MiningBeneficiaryCalculator miningBeneficiaryCalculator;
public PrivateGroupRehydrationBlockProcessor(
@ -70,13 +68,15 @@ public class PrivateGroupRehydrationBlockProcessor {
final AbstractBlockProcessor.TransactionReceiptFactory transactionReceiptFactory,
final Wei blockReward,
final MiningBeneficiaryCalculator miningBeneficiaryCalculator,
final boolean skipZeroBlockRewards) {
final boolean skipZeroBlockRewards,
final PrivateStateGenesisAllocator privateStateGenesisAllocator) {
this.transactionProcessor = transactionProcessor;
this.privateTransactionProcessor = privateTransactionProcessor;
this.transactionReceiptFactory = transactionReceiptFactory;
this.blockReward = blockReward;
this.miningBeneficiaryCalculator = miningBeneficiaryCalculator;
this.skipZeroBlockRewards = skipZeroBlockRewards;
this.privateStateGenesisAllocator = privateStateGenesisAllocator;
}
public AbstractBlockProcessor.Result processBlock(
@ -122,9 +122,16 @@ public class PrivateGroupRehydrationBlockProcessor {
final MutableWorldState disposablePrivateState =
privateWorldStateArchive.getMutable(lastRootHash, null).get();
final WorldUpdater privateStateUpdater = disposablePrivateState.updater();
maybeInjectDefaultManagementAndProxy(
lastRootHash, disposablePrivateState, privateStateUpdater);
final WorldUpdater privateWorldStateUpdater = disposablePrivateState.updater();
if (lastRootHash.equals(EMPTY_ROOT_HASH)) {
privateStateGenesisAllocator.applyGenesisToPrivateWorldState(
disposablePrivateState,
privateWorldStateUpdater,
privacyGroupId,
blockHeader.getNumber());
}
LOG.debug(
"Pre-rehydrate root hash: {} for tx {}",
disposablePrivateState.rootHash(),
@ -134,7 +141,7 @@ public class PrivateGroupRehydrationBlockProcessor {
privateTransactionProcessor.processTransaction(
blockchain,
worldStateUpdater.updater(),
privateStateUpdater,
privateWorldStateUpdater,
blockHeader,
transactionHash,
privateTransaction,
@ -143,7 +150,7 @@ public class PrivateGroupRehydrationBlockProcessor {
new BlockHashLookup(blockHeader, blockchain),
privateTransaction.getPrivacyGroupId().get());
privateStateUpdater.commit();
privateWorldStateUpdater.commit();
disposablePrivateState.persist(null);
storePrivateMetadata(
@ -208,35 +215,6 @@ public class PrivateGroupRehydrationBlockProcessor {
new PrivateTransactionMetadata(commitmentHash, disposablePrivateState.rootHash()));
}
protected void maybeInjectDefaultManagementAndProxy(
final Hash lastRootHash,
final MutableWorldState disposablePrivateState,
final WorldUpdater privateWorldStateUpdater) {
if (lastRootHash.equals(EMPTY_ROOT_HASH)) {
// inject management
final EvmAccount managementPrecompile =
privateWorldStateUpdater.createAccount(Address.DEFAULT_ONCHAIN_PRIVACY_MANAGEMENT);
final MutableAccount mutableManagementPrecompiled = managementPrecompile.getMutable();
// this is the code for the simple management contract
mutableManagementPrecompiled.setCode(
OnChainGroupManagement.DEFAULT_GROUP_MANAGEMENT_RUNTIME_BYTECODE);
// inject proxy
final EvmAccount proxyPrecompile =
privateWorldStateUpdater.createAccount(Address.ONCHAIN_PRIVACY_PROXY);
final MutableAccount mutableProxyPrecompiled = proxyPrecompile.getMutable();
// this is the code for the proxy contract
mutableProxyPrecompiled.setCode(OnChainGroupManagement.PROXY_RUNTIME_BYTECODE);
// manually set the management contract address so the proxy can trust it
mutableProxyPrecompiled.setStorageValue(
UInt256.ZERO,
UInt256.fromBytes(Bytes32.leftPad(Address.DEFAULT_ONCHAIN_PRIVACY_MANAGEMENT)));
privateWorldStateUpdater.commit();
disposablePrivateState.persist(null);
}
}
private boolean rewardCoinbase(
final MutableWorldState worldState,
final ProcessableBlockHeader header,

@ -0,0 +1,113 @@
/*
* 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.privacy;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.MutableAccount;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.core.WorldUpdater;
import org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement;
import org.hyperledger.besu.plugin.data.PrivacyGenesis;
import org.hyperledger.besu.plugin.services.privacy.PrivacyGroupGenesisProvider;
import java.math.BigInteger;
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 PrivateStateGenesisAllocator {
private static final Logger LOG = LogManager.getLogger();
private final Boolean isOnchainPrivacyEnabled;
private final PrivacyGroupGenesisProvider privacyGroupGenesisProvider;
public PrivateStateGenesisAllocator(
final Boolean isOnchainPrivacyEnabled,
final PrivacyGroupGenesisProvider privacyGroupGenesisProvider) {
this.isOnchainPrivacyEnabled = isOnchainPrivacyEnabled;
this.privacyGroupGenesisProvider = privacyGroupGenesisProvider;
}
public void applyGenesisToPrivateWorldState(
final MutableWorldState disposablePrivateState,
final WorldUpdater privateWorldStateUpdater,
final Bytes privacyGroupId,
final long blockNumber) {
final PrivacyGenesis genesis =
privacyGroupGenesisProvider.getPrivacyGenesis(privacyGroupId, blockNumber);
if (genesis.getAccounts().size() > 0) {
LOG.debug(
"Applying {} privacy accounts onto {} private state genesis at {}",
genesis.getAccounts().size(),
privacyGroupId,
blockNumber);
genesis
.getAccounts()
.forEach(
(genesisAccount) -> {
final Address address = Address.fromPlugin(genesisAccount.getAddress());
if (address.toBigInteger().compareTo(BigInteger.valueOf(Byte.MAX_VALUE)) < 0) {
LOG.warn(
"Genesis address {} is in reserved range and may be overwritten", address);
}
final MutableAccount account =
privateWorldStateUpdater.createAccount(address).getMutable();
LOG.debug("{} applied to genesis", address.toHexString());
account.setNonce(genesisAccount.getNonce());
account.setBalance(Wei.fromQuantity(genesisAccount.getBalance()));
account.setCode(genesisAccount.getCode());
account.setVersion(genesisAccount.getVersion());
genesisAccount.getStorage().forEach(account::setStorageValue);
});
}
if (isOnchainPrivacyEnabled) {
// inject management
final MutableAccount managementContract =
privateWorldStateUpdater
.createAccount(Address.DEFAULT_ONCHAIN_PRIVACY_MANAGEMENT)
.getMutable();
// this is the code for the simple management contract
managementContract.setCode(OnChainGroupManagement.DEFAULT_GROUP_MANAGEMENT_RUNTIME_BYTECODE);
// inject proxy
final MutableAccount procyContract =
privateWorldStateUpdater.createAccount(Address.ONCHAIN_PRIVACY_PROXY).getMutable();
// this is the code for the proxy contract
procyContract.setCode(OnChainGroupManagement.PROXY_RUNTIME_BYTECODE);
// manually set the management contract address so the proxy can trust it
procyContract.setStorageValue(
UInt256.ZERO,
UInt256.fromBytes(Bytes32.leftPad(Address.DEFAULT_ONCHAIN_PRIVACY_MANAGEMENT)));
}
privateWorldStateUpdater.commit();
disposablePrivateState.persist(null);
}
}

@ -47,6 +47,7 @@ public class PrivateStateRehydration {
private final WorldStateArchive publicWorldStateArchive;
private final WorldStateArchive privateWorldStateArchive;
private final PrivateStateRootResolver privateStateRootResolver;
private final PrivateStateGenesisAllocator privateStateGenesisAllocator;
public PrivateStateRehydration(
final PrivateStateStorage privateStateStorage,
@ -54,13 +55,15 @@ public class PrivateStateRehydration {
final ProtocolSchedule protocolSchedule,
final WorldStateArchive publicWorldStateArchive,
final WorldStateArchive privateWorldStateArchive,
final PrivateStateRootResolver privateStateRootResolver) {
final PrivateStateRootResolver privateStateRootResolver,
final PrivateStateGenesisAllocator privateStateGenesisAllocator) {
this.privateStateStorage = privateStateStorage;
this.blockchain = blockchain;
this.protocolSchedule = protocolSchedule;
this.publicWorldStateArchive = publicWorldStateArchive;
this.privateWorldStateArchive = privateWorldStateArchive;
this.privateStateRootResolver = privateStateRootResolver;
this.privateStateGenesisAllocator = privateStateGenesisAllocator;
}
public void rehydrate(
@ -145,7 +148,8 @@ public class PrivateStateRehydration {
protocolSpec.getTransactionReceiptFactory(),
protocolSpec.getBlockReward(),
protocolSpec.getMiningBeneficiaryCalculator(),
protocolSpec.isSkipZeroBlockRewards());
protocolSpec.isSkipZeroBlockRewards(),
privateStateGenesisAllocator);
final MutableWorldState publicWorldState =
blockchain

@ -40,7 +40,7 @@ public class DefaultMutablePrivateWorldStateUpdater implements WorldUpdater {
@Override
public EvmAccount createAccount(final Address address, final long nonce, final Wei balance) {
return privateWorldUpdater.createAccount(address);
return privateWorldUpdater.createAccount(address, nonce, balance);
}
@Override

@ -37,6 +37,7 @@ import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.core.WorldUpdater;
import org.hyperledger.besu.ethereum.core.WrappedEvmAccount;
import org.hyperledger.besu.ethereum.privacy.PrivateStateGenesisAllocator;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
@ -79,7 +80,9 @@ public class PrivacyBlockProcessorTest {
enclave,
privateStateStorage,
privateWorldStateArchive,
new PrivateStateRootResolver(privateStateStorage));
new PrivateStateRootResolver(privateStateStorage),
new PrivateStateGenesisAllocator(
true, (privacyGroupId, blockNumber) -> Collections::emptyList));
publicWorldStateArchive = mock(WorldStateArchive.class);
privacyBlockProcessor.setPublicWorldStateArchive(publicWorldStateArchive);
}

@ -40,6 +40,7 @@ import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.WorldUpdater;
import org.hyperledger.besu.ethereum.mainnet.SpuriousDragonGasCalculator;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.privacy.PrivateStateGenesisAllocator;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
@ -82,6 +83,9 @@ public class OnChainPrivacyPrecompiledContractTest {
final PrivateStateRootResolver privateStateRootResolver =
new PrivateStateRootResolver(privateStateStorage);
PrivateStateGenesisAllocator privateStateGenesisAllocator =
mock(PrivateStateGenesisAllocator.class);
private PrivateTransactionProcessor mockPrivateTxProcessor(
final TransactionProcessingResult result) {
final PrivateTransactionProcessor mockPrivateTransactionProcessor =
@ -162,7 +166,6 @@ public class OnChainPrivacyPrecompiledContractTest {
when(enclave.receive(any())).thenReturn(response);
final OnChainPrivacyPrecompiledContract contractSpy = spy(contract);
Mockito.doNothing().when(contractSpy).maybeInjectDefaultManagementAndProxy(any(), any(), any());
Mockito.doReturn(true)
.when(contractSpy)
.canExecute(any(), any(), any(), any(), any(), any(), any(), any(), any(), any());
@ -234,7 +237,6 @@ public class OnChainPrivacyPrecompiledContractTest {
final Enclave enclave = mock(Enclave.class);
final OnChainPrivacyPrecompiledContract contract = buildPrivacyPrecompiledContract(enclave);
final OnChainPrivacyPrecompiledContract contractSpy = spy(contract);
Mockito.doNothing().when(contractSpy).maybeInjectDefaultManagementAndProxy(any(), any(), any());
Mockito.doReturn(false)
.when(contractSpy)
.isContractLocked(any(), any(), any(), any(), any(), any(), any());
@ -272,7 +274,8 @@ public class OnChainPrivacyPrecompiledContractTest {
new SpuriousDragonGasCalculator(),
enclave,
worldStateArchive,
privateStateRootResolver);
privateStateRootResolver,
privateStateGenesisAllocator);
contract.setPrivateTransactionProcessor(
mockPrivateTxProcessor(
@ -280,7 +283,6 @@ public class OnChainPrivacyPrecompiledContractTest {
ValidationResult.invalid(TransactionInvalidReason.INCORRECT_NONCE))));
final OnChainPrivacyPrecompiledContract contractSpy = spy(contract);
Mockito.doNothing().when(contractSpy).maybeInjectDefaultManagementAndProxy(any(), any(), any());
Mockito.doReturn(true)
.when(contractSpy)
.canExecute(any(), any(), any(), any(), any(), any(), any(), any(), any(), any());
@ -310,6 +312,10 @@ public class OnChainPrivacyPrecompiledContractTest {
private OnChainPrivacyPrecompiledContract buildPrivacyPrecompiledContract(final Enclave enclave) {
return new OnChainPrivacyPrecompiledContract(
new SpuriousDragonGasCalculator(), enclave, worldStateArchive, privateStateRootResolver);
new SpuriousDragonGasCalculator(),
enclave,
worldStateArchive,
privateStateRootResolver,
privateStateGenesisAllocator);
}
}

@ -47,12 +47,15 @@ import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import org.hyperledger.besu.ethereum.vm.OperationTracer;
import org.hyperledger.besu.plugin.data.PrivacyGenesis;
import org.hyperledger.besu.plugin.services.PrivacyPluginService;
import org.hyperledger.besu.plugin.services.privacy.PrivacyGroupAuthProvider;
import org.hyperledger.besu.plugin.services.privacy.PrivacyGroupGenesisProvider;
import org.hyperledger.besu.plugin.services.privacy.PrivacyPluginPayloadProvider;
import org.hyperledger.besu.plugin.services.privacy.PrivateMarkerTransactionFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@ -135,6 +138,16 @@ public class PrivacyPluginPrecompiledContractTest {
return (privacyGroupId, privacyUserId, blockNumber) -> true;
}
@Override
public void setPrivacyGroupGenesisProvider(
final PrivacyGroupGenesisProvider privacyGroupAuthProvider) {}
@Override
public PrivacyGroupGenesisProvider getPrivacyGroupGenesisProvider() {
return (privacyGroupId, blockNumber) ->
(PrivacyGenesis) Collections::emptyList;
}
@Override
public PrivateMarkerTransactionFactory getPrivateMarkerTransactionFactory() {
return null;

@ -42,6 +42,7 @@ import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.WorldUpdater;
import org.hyperledger.besu.ethereum.mainnet.SpuriousDragonGasCalculator;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.privacy.PrivateStateGenesisAllocator;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
@ -85,6 +86,10 @@ public class PrivacyPrecompiledContractTest {
final PrivateStateRootResolver privateStateRootResolver =
new PrivateStateRootResolver(privateStateStorage);
final PrivateStateGenesisAllocator privateStateGenesisAllocator =
new PrivateStateGenesisAllocator(
false, (privacyGroupId, blockNumber) -> Collections::emptyList);
private PrivateTransactionProcessor mockPrivateTxProcessor(
final TransactionProcessingResult result) {
final PrivateTransactionProcessor mockPrivateTransactionProcessor =
@ -284,7 +289,9 @@ public class PrivacyPrecompiledContractTest {
new SpuriousDragonGasCalculator(),
enclave,
worldStateArchive,
privateStateRootResolver);
privateStateRootResolver,
privateStateGenesisAllocator,
"RestrictedPrivacyTest");
contract.setPrivateTransactionProcessor(
mockPrivateTxProcessor(
@ -308,7 +315,7 @@ public class PrivacyPrecompiledContractTest {
@Test
public void testSimulatedPublicTransactionIsSkipped() {
final PrivacyPrecompiledContract emptyContract =
new PrivacyPrecompiledContract(null, null, null, null);
new PrivacyPrecompiledContract(null, null, null, null, null, null);
// A simulated public transaction doesn't contain a PrivateMetadataUpdater
final MessageFrame frame = mock(MessageFrame.class);
@ -327,6 +334,11 @@ public class PrivacyPrecompiledContractTest {
private PrivacyPrecompiledContract buildPrivacyPrecompiledContract(final Enclave enclave) {
return new PrivacyPrecompiledContract(
new SpuriousDragonGasCalculator(), enclave, worldStateArchive, privateStateRootResolver);
new SpuriousDragonGasCalculator(),
enclave,
worldStateArchive,
privateStateRootResolver,
privateStateGenesisAllocator,
"PrivacyTests");
}
}

@ -0,0 +1,160 @@
/*
* 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.privacy;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.core.Address.DEFAULT_ONCHAIN_PRIVACY_MANAGEMENT;
import static org.hyperledger.besu.ethereum.core.Address.ONCHAIN_PRIVACY_PROXY;
import static org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement.DEFAULT_GROUP_MANAGEMENT_RUNTIME_BYTECODE;
import static org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement.PROXY_RUNTIME_BYTECODE;
import org.hyperledger.besu.ethereum.core.Account;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.core.WorldUpdater;
import org.hyperledger.besu.ethereum.trie.MerklePatriciaTrie;
import org.hyperledger.besu.plugin.data.PrivacyGenesis;
import org.hyperledger.besu.plugin.data.PrivacyGenesisAccount;
import org.hyperledger.besu.plugin.data.Quantity;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
import org.junit.Test;
public class PrivateStateGenesisAllocatorTest {
public static final Hash EMPTY_ROOT_HASH = Hash.wrap(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH);
private final MutableWorldState worldState =
InMemoryKeyValueStorageProvider.createInMemoryWorldState();
final WorldUpdater updater = worldState.updater();
Address genesisAddress = Address.fromHexString("0x1000000000000000000000000000000000000001");
PrivacyGenesis privacyGenesis =
() ->
List.of(
new PrivacyGenesisAccount() {
@Override
public org.hyperledger.besu.plugin.data.Address getAddress() {
return genesisAddress;
}
@Override
public Map<UInt256, UInt256> getStorage() {
return Collections.emptyMap();
}
@Override
public int getVersion() {
return 0;
}
@Override
public Long getNonce() {
return 0L;
}
@Override
public Quantity getBalance() {
return Wei.ONE;
}
@Override
public Bytes getCode() {
return Bytes.fromHexString("0x42");
}
});
@Test
public void whenOnChainDisabledAndNoAccountsProvidedNoGenesisIsApplied() {
PrivateStateGenesisAllocator privateStateGenesisAllocator =
new PrivateStateGenesisAllocator(
false, (privacyGroupId, blockNumber) -> Collections::emptyList);
privateStateGenesisAllocator.applyGenesisToPrivateWorldState(
worldState, updater, Bytes.EMPTY, 0);
assertThat(worldState.frontierRootHash()).isEqualTo(EMPTY_ROOT_HASH);
}
@Test
public void whenOnChainEnabledAndNoAccountsProvidedPrivacyManagementContractIsApplied() {
PrivateStateGenesisAllocator privateStateGenesisAllocator =
new PrivateStateGenesisAllocator(
true, (privacyGroupId, blockNumber) -> Collections::emptyList);
privateStateGenesisAllocator.applyGenesisToPrivateWorldState(
worldState, updater, Bytes.EMPTY, 0);
assertThat(worldState.frontierRootHash()).isNotEqualTo(EMPTY_ROOT_HASH);
assertManagementContractApplied();
}
@Test
public void whenOnChainEnabledAndAccountsProvidedPrivacyManagementContractAndGenesisIsApplied() {
PrivateStateGenesisAllocator privateStateGenesisAllocator =
new PrivateStateGenesisAllocator(true, (privacyGroupId, blockNumber) -> privacyGenesis);
privateStateGenesisAllocator.applyGenesisToPrivateWorldState(
worldState, updater, Bytes.EMPTY, 0);
assertThat(worldState.frontierRootHash()).isNotEqualTo(EMPTY_ROOT_HASH);
assertManagementContractApplied();
assertGenesisAccountApplied();
}
@Test
public void whenOnChainDisabledAndAccountsProvidedPrivacyManagementContractAndGenesisIsApplied() {
PrivateStateGenesisAllocator privateStateGenesisAllocator =
new PrivateStateGenesisAllocator(false, (privacyGroupId, blockNumber) -> privacyGenesis);
privateStateGenesisAllocator.applyGenesisToPrivateWorldState(
worldState, updater, Bytes.EMPTY, 0);
assertThat(worldState.frontierRootHash()).isNotEqualTo(EMPTY_ROOT_HASH);
assertThat(worldState.get(ONCHAIN_PRIVACY_PROXY)).isEqualTo(null);
assertGenesisAccountApplied();
}
private void assertGenesisAccountApplied() {
Account genesisAccount = worldState.get(genesisAddress);
assertThat(genesisAccount.getCode()).isEqualTo(Bytes.fromHexString("0x42"));
assertThat(genesisAccount.getBalance()).isEqualTo(Wei.ONE);
}
private void assertManagementContractApplied() {
Account managementProxy = worldState.get(ONCHAIN_PRIVACY_PROXY);
assertThat(managementProxy.getCode()).isEqualTo(PROXY_RUNTIME_BYTECODE);
assertThat(managementProxy.getStorageValue(UInt256.ZERO))
.isEqualTo(UInt256.fromBytes(DEFAULT_ONCHAIN_PRIVACY_MANAGEMENT));
Account managementContract = worldState.get(DEFAULT_ONCHAIN_PRIVACY_MANAGEMENT);
assertThat(managementContract.getCode()).isEqualTo(DEFAULT_GROUP_MANAGEMENT_RUNTIME_BYTECODE);
}
}

@ -64,7 +64,7 @@ Calculated : ${currentHash}
tasks.register('checkAPIChanges', FileStateChecker) {
description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
files = sourceSets.main.allJava.files
knownHash = 'jzeZ7YS9/4K+Q4TZMbrUlkDnDce6mCRisy1CoCTxoa8='
knownHash = '2JAuicCNps4APcYpEbWHqNOawoJAakVi19pPI5NwvsE='
}
check.dependsOn('checkAPIChanges')

@ -0,0 +1,27 @@
/*
* 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.plugin.data;
import java.util.List;
/** An interface to return the private state genesis. */
public interface PrivacyGenesis {
/**
* A list of pre-compiled wallets with associated information.
*
* @return The list of addresses, possibly empty. Should never be null.
*/
List<PrivacyGenesisAccount> getAccounts();
}

@ -0,0 +1,66 @@
/*
* 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.plugin.data;
import java.util.Map;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
public interface PrivacyGenesisAccount {
/**
* The address of the initial genesis allocation/pre-compile
*
* @return Ethereum account address
*/
Address getAddress();
/**
* Any storage that the address requires
*
* @return account storage
*/
Map<UInt256, UInt256> getStorage();
/**
* The version for the account.
*
* @return the version of the code being set
*/
int getVersion();
/**
* The initial nonce assigned to the account.
*
* @return the nonce
*/
Long getNonce();
/**
* The initial balance assigned to the account.
*
* @return the balance
*/
Quantity getBalance();
/**
* The initial code for the account.
*
* <p>Note! this must be the contract code and not the code to deploy the contract.
*
* @return the wallet code. Can be null or 0x
*/
Bytes getCode();
}

@ -17,7 +17,7 @@ package org.hyperledger.besu.plugin.data;
import java.math.BigInteger;
/**
* An interface to mark objects that also represents a disceete quantity, such as an unsigned
* An interface to mark objects that also represents a discrete quantity, such as an unsigned
* integer value.
*/
public interface Quantity {

@ -15,20 +15,56 @@
package org.hyperledger.besu.plugin.services;
import org.hyperledger.besu.plugin.services.privacy.PrivacyGroupAuthProvider;
import org.hyperledger.besu.plugin.services.privacy.PrivacyGroupGenesisProvider;
import org.hyperledger.besu.plugin.services.privacy.PrivacyPluginPayloadProvider;
import org.hyperledger.besu.plugin.services.privacy.PrivateMarkerTransactionFactory;
/**
* A service that plugins can use to define how private transactions should be handled. <br>
* <br>
* You must register a {@link PrivacyPluginPayloadProvider} when using this plugin and can
* optionally register a {@link PrivateMarkerTransactionFactory} and a {@link
* PrivacyGroupGenesisProvider}
*/
public interface PrivacyPluginService extends BesuService {
/**
* Register a provider to use when handling privacy marker transactions.
*
* @param privacyPluginPayloadProvider the provider to use for the privacy marker payload
*/
void setPayloadProvider(PrivacyPluginPayloadProvider privacyPluginPayloadProvider);
PrivacyPluginPayloadProvider getPayloadProvider();
/**
* Register a factory to specify your own method for signing and serializing privacy marker
* transactions.
*
* @param privateMarkerTransactionFactory the factory to use to build the privacy marker
* transaction
*/
void setPrivateMarkerTransactionFactory(
PrivateMarkerTransactionFactory privateMarkerTransactionFactory);
PrivateMarkerTransactionFactory getPrivateMarkerTransactionFactory();
/**
* Register a provider to use when auth requests for a multi-tenant environment. If you are not
* using a multi-tenant environment you always return true.
*
* @param privacyGroupAuthProvider the provider to use to determine authz
*/
void setPrivacyGroupAuthProvider(PrivacyGroupAuthProvider privacyGroupAuthProvider);
PrivacyGroupAuthProvider getPrivacyGroupAuthProvider();
PrivateMarkerTransactionFactory getPrivateMarkerTransactionFactory();
/**
* Register a provider for initialising private state genesis
*
* @param privacyGroupAuthProvider the provider for the initial private state
*/
void setPrivacyGroupGenesisProvider(PrivacyGroupGenesisProvider privacyGroupAuthProvider);
void setPrivateMarkerTransactionFactory(
PrivateMarkerTransactionFactory privateMarkerTransactionFactory);
PrivacyGroupGenesisProvider getPrivacyGroupGenesisProvider();
}

@ -16,7 +16,18 @@ package org.hyperledger.besu.plugin.services.privacy;
import java.util.Optional;
/**
* When in a multi-tenant environment you need to decided if an authz user can access a privacyGroup
*/
@FunctionalInterface
public interface PrivacyGroupAuthProvider {
/**
* Should this privacyUserId be able to access this privacyGroupId at this blockNumber
*
* @param privacyGroupId the privacyGroupId
* @param privacyUserId the authz privacyUserId when in a multi-tenant environment
* @param blockNumber the block height it's happening at
* @return if the can access that privacyUserId
*/
boolean canAccess(String privacyGroupId, String privacyUserId, Optional<Long> blockNumber);
}

@ -0,0 +1,32 @@
/*
* 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.plugin.services.privacy;
import org.hyperledger.besu.plugin.data.PrivacyGenesis;
import org.apache.tuweni.bytes.Bytes;
/** A way to initiate private state with a genesis */
@FunctionalInterface
public interface PrivacyGroupGenesisProvider {
/**
* Allows you to specify a custom private genesis to apply when initialising a privacy group
*
* @param privacyGroupId the privacyGroupId
* @param blockNumber the block height
* @return the privacy genesis to apply
*/
PrivacyGenesis getPrivacyGenesis(Bytes privacyGroupId, long blockNumber);
}

@ -21,8 +21,34 @@ import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
/**
* Allows you to register a provider that will dictate how the payload of a privacy marker
* transaction is handled.
*/
public interface PrivacyPluginPayloadProvider {
/**
* Given a private transaction what information should be stored on chain in the payload of the
* privacy marker transaction? At it's simplest you could just serialize the privateTransaction
* using RLP. This method is called when the user has submitted a private transaction using
* `eea_sendRawTransaction`. If you do not know how to serialize the PrivateTransaction you should
* throw an exception so that an error is returned to the user.
*
* @param privateTransaction the initial private transaction
* @param privacyUserId the user id - only used in multi-tenant environments.
* @return the raw bytes of what should be stored in {@link Transaction#getPayload()}
*/
Bytes generateMarkerPayload(PrivateTransaction privateTransaction, String privacyUserId);
/**
* When processing privacy marker transactions besu needs to know how to process them. The plugin
* must perform the deserialization steps required. If the node shouldn't have access to that
* transaction return Optional.empty().<br>
* <br>
* Note! Any exceptions thrown in this method with cause besu to crash and stop processing blocks
*
* @param transaction the privacy marker transaction
* @return The PrivateTransaction to apply to private world state.
*/
Optional<PrivateTransaction> getPrivateTransactionFromPayload(Transaction transaction);
}

@ -20,10 +20,29 @@ import org.hyperledger.besu.plugin.data.UnsignedPrivateMarkerTransaction;
import org.apache.tuweni.bytes.Bytes;
/** Implement an instance of this for signing and serializing privacy marker transactions. */
public interface PrivateMarkerTransactionFactory {
/**
* We need this from the plugin so that we can provide the correct nonce in {@link
* #create(UnsignedPrivateMarkerTransaction, PrivateTransaction, String)}
*
* @param privateTransaction the transaction about to be submitted to the transaction pool
* @param privacyUserId the authz privacyUserId when in a multi-tenant environment
* @return sender address
*/
Address getSender(PrivateTransaction privateTransaction, String privacyUserId);
/**
* You need to return a raw signed transaction. This will be submitted directly into the
* transaction pool. You can use the information provided in {@link
* UnsignedPrivateMarkerTransaction} to sign and serialize the transaction or forge your own.
*
* @param unsignedPrivateMarkerTransaction the unsigned privacy marker transaction
* @param privateTransaction the original private transaction
* @param privacyUserId the authz privacyUserId when in a multi-tenant environment
* @return signed raw transaction
*/
Bytes create(
UnsignedPrivateMarkerTransaction unsignedPrivateMarkerTransaction,
PrivateTransaction privateTransaction,

Loading…
Cancel
Save