Skip to content

Commit

Permalink
Privacy plugin pmt factory (hyperledger#2557)
Browse files Browse the repository at this point in the history
* Refactor: clarify intent of arg when testing permissioning plugin

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

* Refactor: remove duplicate createPrivateMarkerTransaction

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

* PrivateMarkerTransactionFactory: add option to delegate to plugin

if defined the plugin will be responsible for creating the pmt

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

* Implement locking strategy for eea_sendRawTransaction

- this will lock per address being sent to prevent nonce
too low errors

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

* Refactor plugin integration to use a more explicit poco

- the plugin will need to sign the transaction with the data it's given
- it will not need to query anything e.g nonce etc

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

* refactor: update naming and tidy up based on comments

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

* Isolate code for calculating gas limit when using privacy plugin

Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>
Co-authored-by: Vijay Michalik <vijay.michalik@consensys.net>
  • Loading branch information
antonydenyer and Vijay Michalik authored Jul 28, 2021
1 parent bdb9c1a commit 41521b6
Show file tree
Hide file tree
Showing 47 changed files with 829 additions and 520 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class PrivacyNodeConfiguration {
this(false, false, false, besuConfig, keyConfig);
}

PrivacyNodeConfiguration(
public PrivacyNodeConfiguration(
final boolean isOnchainPrivacyGroupEnabled,
final boolean isMultitenancyEnabled,
final boolean isPrivacyPluginEnabled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public PrivacyNodeFactory(final Vertx vertx) {
this.vertx = vertx;
}

private PrivacyNode create(
public PrivacyNode create(
final PrivacyNodeConfiguration privacyNodeConfig,
final EnclaveType enclaveType,
final Optional<Network> containerNetwork)
Expand Down
1 change: 1 addition & 0 deletions acceptance-tests/test-plugins/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ dependencies {
implementation project(':plugin-api')
implementation project(':besu')
implementation project(':ethereum:core')
implementation project(':crypto')
implementation project(':ethereum:rlp')
implementation 'com.google.auto.service:auto-service'
implementation 'info.picocli:picocli'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,6 @@ private boolean transactionMessage(final int code) {
@Override
public void stop() {}

@Option(names = "--plugin-permissioning-enabled")
@Option(names = "--plugin-permissioning-test-enabled")
boolean enabled = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,17 @@
public class TestPrivacyServicePlugin implements BesuPlugin {
private static final Logger LOG = LogManager.getLogger();

private PrivacyPluginService service;
PrivacyPluginService pluginService;
BesuContext context;

@Override
public void register(final BesuContext context) {
this.context = context;

context.getService(PicoCLIOptions.class).get().addPicoCLIOptions("privacy-service", this);
service = context.getService(PrivacyPluginService.class).get();
pluginService = context.getService(PrivacyPluginService.class).get();

service.setPayloadProvider(
pluginService.setPayloadProvider(
new PrivacyPluginPayloadProvider() {
@Override
public Bytes generateMarkerPayload(
Expand Down Expand Up @@ -74,11 +77,22 @@ public Optional<PrivateTransaction> getPrivateTransactionFromPayload(
}

@Override
public void start() {}
public void start() {
if (signingEnabled) {
pluginService.setPrivateMarkerTransactionFactory(
new TestSigningPrivateMarkerTransactionFactory(signingKey));
}
}

@Override
public void stop() {}

@Option(names = "--plugin-privacy-service-encryption-prefix")
String prefix;

@Option(names = "--plugin-privacy-service-signing-enabled")
boolean signingEnabled = false;

@Option(names = "--plugin-privacy-service-signing-key")
String signingKey;
}
Original file line number Diff line number Diff line change
@@ -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;

import static org.apache.logging.log4j.LogManager.getLogger;
import static org.hyperledger.besu.ethereum.core.Address.extract;

import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SECPPrivateKey;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.plugin.data.Address;
import org.hyperledger.besu.plugin.data.PrivateTransaction;
import org.hyperledger.besu.plugin.data.TransactionType;
import org.hyperledger.besu.plugin.data.UnsignedPrivateMarkerTransaction;
import org.hyperledger.besu.plugin.services.privacy.PrivateMarkerTransactionFactory;

import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;

public class TestSigningPrivateMarkerTransactionFactory implements PrivateMarkerTransactionFactory {

private static final Logger LOG = getLogger();

final KeyPair aliceFixedSigningKey;
final Address sender;

public TestSigningPrivateMarkerTransactionFactory(
final String privateMarkerTransactionSigningKey) {
final SignatureAlgorithm algorithm = SignatureAlgorithmFactory.getInstance();
final SECPPrivateKey privateKey =
algorithm.createPrivateKey(Bytes32.fromHexString(privateMarkerTransactionSigningKey));

aliceFixedSigningKey = algorithm.createKeyPair(privateKey);
sender = extract(Hash.hash(aliceFixedSigningKey.getPublicKey().getEncodedBytes()));
}

@Override
public Bytes create(
final UnsignedPrivateMarkerTransaction unsignedPrivateMarkerTransaction,
final PrivateTransaction privateTransaction,
final String privacyUserId) {

final Transaction transaction =
Transaction.builder()
.type(TransactionType.FRONTIER)
.nonce(unsignedPrivateMarkerTransaction.getNonce())
.gasPrice(
unsignedPrivateMarkerTransaction.getGasPrice().map(Wei::fromQuantity).orElse(null))
.gasLimit(unsignedPrivateMarkerTransaction.getGasLimit())
.to(
org.hyperledger.besu.ethereum.core.Address.fromPlugin(
unsignedPrivateMarkerTransaction.getTo().get()))
.value(Wei.fromQuantity(unsignedPrivateMarkerTransaction.getValue()))
.payload(unsignedPrivateMarkerTransaction.getPayload())
.signAndBuild(aliceFixedSigningKey);

LOG.info("Signing PMT from " + sender);

final BytesValueRLPOutput out = new BytesValueRLPOutput();
transaction.writeTo(out);
return out.encoded();
}

@Override
public Address getSender(
final PrivateTransaction privateTransaction, final String privacyUserId) {
return sender;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void setUp() throws Exception {
new BesuNodeConfigurationBuilder()
.miningEnabled(false)
.plugins(List.of("testPlugins"))
.extraCLIOptions(List.of("--plugin-permissioning-enabled=true"))
.extraCLIOptions(List.of("--plugin-permissioning-test-enabled=true"))
.jsonRpcEnabled()
.jsonRpcTxPool()
.jsonRpcAdmin();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* 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.hyperledger.besu.tests.acceptance.dsl.privacy.account.PrivacyAccountResolver.BOB;
import static org.web3j.utils.Restriction.UNRESTRICTED;

import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.BesuNodeConfigurationBuilder;
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.privacy.PrivacyNodeConfiguration;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyAcceptanceTestBase;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyNode;
import org.hyperledger.besu.tests.web3j.generated.EventEmitter;
import org.hyperledger.enclave.testutil.EnclaveKeyConfiguration;
import org.hyperledger.enclave.testutil.EnclaveType;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import org.junit.Before;
import org.junit.Test;
import org.web3j.protocol.core.methods.response.TransactionReceipt;

public class PluginPrivacySigningAcceptanceTest extends PrivacyAcceptanceTestBase {
private PrivacyNode minerNode;

@Before
public void setup() throws IOException {
minerNode =
privacyBesu.create(
new PrivacyNodeConfiguration(
false,
false,
true,
new BesuNodeConfigurationBuilder()
.name("miner")
.miningEnabled()
.jsonRpcEnabled()
.webSocketEnabled()
.enablePrivateTransactions()
.keyFilePath(BOB.getPrivateKeyPath())
.plugins(Collections.singletonList("testPlugins"))
.extraCLIOptions(
List.of(
"--plugin-privacy-service-encryption-prefix=0xAA",
"--plugin-privacy-service-signing-enabled=true",
"--plugin-privacy-service-signing-key=8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63"))
.build(),
new EnclaveKeyConfiguration(
BOB.getEnclaveKeyPaths(), BOB.getEnclavePrivateKeyPaths())),
EnclaveType.NOOP,
Optional.empty());

privacyCluster.start(minerNode);
}

@Test
public void canDeployContractSignedByPlugin() throws Exception {
final String contractAddress = "0xd0152772c54cecfa7684f09f7616dcc825545dff";

final EventEmitter eventEmitter =
minerNode.execute(
privateContractTransactions.createSmartContract(
EventEmitter.class,
minerNode.getTransactionSigningKey(),
UNRESTRICTED,
minerNode.getEnclaveKey()));

privateContractVerifier
.validPrivateContractDeployed(contractAddress, minerNode.getAddress().toString())
.verify(eventEmitter);
privateContractVerifier.validContractCodeProvided().verify(eventEmitter);

final TransactionReceipt pmtReceipt =
minerNode
.execute(
ethTransactions.getTransactionReceipt(
"0x5586b8321e26cdabd68e0139955b90f97c0fc082519d07e1c0b9db26862ff2ff"))
.get();

assertThat(pmtReceipt.getStatus()).isEqualTo("0x1");

assertThat(pmtReceipt.getFrom()).isEqualTo("0xfe3b557e8fb62b89f4916b721be55ceb828dbd73");
}
}
12 changes: 3 additions & 9 deletions besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -659,8 +659,7 @@ public Runner build() {
webSocketsJsonRpcMethods,
privacyParameters,
protocolSchedule,
blockchainQueries,
transactionPool));
blockchainQueries));

createPrivateTransactionObserver(subscriptionManager, privacyParameters);
}
Expand Down Expand Up @@ -943,20 +942,15 @@ private WebSocketService createWebsocketService(
final Map<String, JsonRpcMethod> jsonRpcMethods,
final PrivacyParameters privacyParameters,
final ProtocolSchedule protocolSchedule,
final BlockchainQueries blockchainQueries,
final TransactionPool transactionPool) {
final BlockchainQueries blockchainQueries) {

final WebSocketMethodsFactory websocketMethodsFactory =
new WebSocketMethodsFactory(subscriptionManager, jsonRpcMethods);

if (privacyParameters.isEnabled()) {
final PrivateWebSocketMethodsFactory privateWebSocketMethodsFactory =
new PrivateWebSocketMethodsFactory(
privacyParameters,
subscriptionManager,
protocolSchedule,
blockchainQueries,
transactionPool);
privacyParameters, subscriptionManager, protocolSchedule, blockchainQueries);

privateWebSocketMethodsFactory.methods().forEach(websocketMethodsFactory::addMethods);
}
Expand Down
Loading

0 comments on commit 41521b6

Please sign in to comment.