mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-08 04:33:56 -05:00
Merge branch 'main' into zkbesu
# Conflicts: # crypto/algorithms/build.gradle # crypto/services/build.gradle # datatypes/build.gradle # ethereum/rlp/build.gradle # evm/build.gradle # plugin-api/build.gradle # util/build.gradle
This commit is contained in:
12
CHANGELOG.md
12
CHANGELOG.md
@@ -4,6 +4,17 @@
|
||||
|
||||
### Upcoming Breaking Changes
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
### Additions and Improvements
|
||||
- Include current chain head block when computing `eth_maxPriorityFeePerGas` [#7485](https://github.com/hyperledger/besu/pull/7485)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
## 24.9.0
|
||||
|
||||
### Upcoming Breaking Changes
|
||||
|
||||
### Breaking Changes
|
||||
- Receipt compaction is enabled by default. It will no longer be possible to downgrade Besu to versions prior to 24.5.1.
|
||||
|
||||
@@ -11,6 +22,7 @@
|
||||
- Add 'inbound' field to admin_peers JSON-RPC Call [#7461](https://github.com/hyperledger/besu/pull/7461)
|
||||
- Add pending block header to `TransactionEvaluationContext` plugin API [#7483](https://github.com/hyperledger/besu/pull/7483)
|
||||
- Add bootnode to holesky config [#7500](https://github.com/hyperledger/besu/pull/7500)
|
||||
- Implement engine_getClientVersionV1 [#7512](https://github.com/hyperledger/besu/pull/7512)
|
||||
- Performance optimzation for ECMUL (1 of 2) [#7509](https://github.com/hyperledger/besu/pull/7509)
|
||||
- Performance optimzation for ECMUL (2 of 2) [#7543](https://github.com/hyperledger/besu/pull/7543)
|
||||
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.hyperledger.enclave.testutil.EnclaveEncryptorType.EC;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveEncryptorType.NACL;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveType.NOOP;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveType.TESSERA;
|
||||
import static org.web3j.utils.Restriction.RESTRICTED;
|
||||
import static org.web3j.utils.Restriction.UNRESTRICTED;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.transaction.PluginCreateRandomPrivacyGroupIdTransaction;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.transaction.RestrictedCreatePrivacyGroupTransaction;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
|
||||
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.web3j.utils.Restriction;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public abstract class ParameterizedEnclaveTestBase extends PrivacyAcceptanceTestBase {
|
||||
protected final Restriction restriction;
|
||||
protected final EnclaveType enclaveType;
|
||||
protected final EnclaveEncryptorType enclaveEncryptorType;
|
||||
|
||||
protected ParameterizedEnclaveTestBase(
|
||||
final Restriction restriction,
|
||||
final EnclaveType enclaveType,
|
||||
final EnclaveEncryptorType enclaveEncryptorType) {
|
||||
this.restriction = restriction;
|
||||
this.enclaveType = enclaveType;
|
||||
this.enclaveEncryptorType = enclaveEncryptorType;
|
||||
}
|
||||
|
||||
@Parameters(name = "{0} tx with {1} enclave and {2} encryptor type")
|
||||
public static Collection<Object[]> params() {
|
||||
return Arrays.asList(
|
||||
new Object[][] {
|
||||
{RESTRICTED, TESSERA, NACL},
|
||||
{RESTRICTED, TESSERA, EC},
|
||||
{UNRESTRICTED, NOOP, EnclaveEncryptorType.NOOP}
|
||||
});
|
||||
}
|
||||
|
||||
public Transaction<String> createPrivacyGroup(
|
||||
final String name, final String description, final PrivacyNode... nodes) {
|
||||
|
||||
if (restriction == RESTRICTED) {
|
||||
return new RestrictedCreatePrivacyGroupTransaction(name, description, nodes);
|
||||
} else if (restriction == UNRESTRICTED) {
|
||||
return new PluginCreateRandomPrivacyGroupIdTransaction();
|
||||
} else {
|
||||
throw new RuntimeException("Do not know how to handle " + restriction);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.WaitUtils;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.eth.EthConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.net.NetConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.priv.PrivConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.privacy.PrivacyNodeFactory;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.condition.PrivateContractVerifier;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.condition.PrivateTransactionVerifier;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.contract.PrivateContractTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.transaction.PrivacyTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.contract.ContractTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.eth.EthTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.net.NetTransactions;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import org.junit.After;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
public class PrivacyAcceptanceTestBase {
|
||||
@ClassRule public static final TemporaryFolder privacy = new TemporaryFolder();
|
||||
|
||||
protected final PrivacyTransactions privacyTransactions;
|
||||
protected final PrivateContractVerifier privateContractVerifier;
|
||||
protected final PrivateTransactionVerifier privateTransactionVerifier;
|
||||
protected final PrivacyNodeFactory privacyBesu;
|
||||
protected final PrivateContractTransactions privateContractTransactions;
|
||||
protected final PrivConditions priv;
|
||||
protected final PrivacyCluster privacyCluster;
|
||||
protected final ContractTransactions contractTransactions;
|
||||
protected final NetConditions net;
|
||||
protected final EthTransactions ethTransactions;
|
||||
protected final EthConditions eth;
|
||||
private final Vertx vertx = Vertx.vertx();
|
||||
|
||||
public PrivacyAcceptanceTestBase() {
|
||||
ethTransactions = new EthTransactions();
|
||||
net = new NetConditions(new NetTransactions());
|
||||
privacyTransactions = new PrivacyTransactions();
|
||||
privateContractVerifier = new PrivateContractVerifier();
|
||||
privateTransactionVerifier = new PrivateTransactionVerifier(privacyTransactions);
|
||||
privacyBesu = new PrivacyNodeFactory(vertx);
|
||||
privateContractTransactions = new PrivateContractTransactions();
|
||||
privacyCluster = new PrivacyCluster(net);
|
||||
priv =
|
||||
new PrivConditions(
|
||||
new org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy
|
||||
.PrivacyTransactions());
|
||||
contractTransactions = new ContractTransactions();
|
||||
eth = new EthConditions(ethTransactions);
|
||||
}
|
||||
|
||||
protected void waitForBlockHeight(final PrivacyNode node, final long blockchainHeight) {
|
||||
WaitUtils.waitFor(
|
||||
120,
|
||||
() ->
|
||||
assertThat(node.execute(ethTransactions.blockNumber()))
|
||||
.isGreaterThanOrEqualTo(BigInteger.valueOf(blockchainHeight)));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDownAcceptanceTestBase() {
|
||||
privacyCluster.close();
|
||||
vertx.close();
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,8 @@ task testPluginsJar(type: Jar) {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
from sourceSets.main.output
|
||||
|
||||
@@ -1,318 +0,0 @@
|
||||
/*
|
||||
* 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 org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyAcceptanceTestBase;
|
||||
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.bft.ConsensusType;
|
||||
import org.hyperledger.besu.tests.web3j.generated.EventEmitter;
|
||||
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.testcontainers.containers.Network;
|
||||
import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
|
||||
import org.web3j.utils.Restriction;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class BftPrivacyClusterAcceptanceTest extends PrivacyAcceptanceTestBase {
|
||||
private final BftPrivacyType bftPrivacyType;
|
||||
|
||||
public static class BftPrivacyType {
|
||||
private final EnclaveType enclaveType;
|
||||
private final EnclaveEncryptorType enclaveEncryptorType;
|
||||
private final ConsensusType consensusType;
|
||||
private final Restriction restriction;
|
||||
|
||||
public BftPrivacyType(
|
||||
final EnclaveType enclaveType,
|
||||
final EnclaveEncryptorType enclaveEncryptorType,
|
||||
final ConsensusType consensusType,
|
||||
final Restriction restriction) {
|
||||
this.enclaveType = enclaveType;
|
||||
this.enclaveEncryptorType = enclaveEncryptorType;
|
||||
this.consensusType = consensusType;
|
||||
this.restriction = restriction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.join(
|
||||
",",
|
||||
enclaveType.toString(),
|
||||
enclaveEncryptorType.toString(),
|
||||
consensusType.toString(),
|
||||
restriction.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public BftPrivacyClusterAcceptanceTest(final BftPrivacyType bftPrivacyType) {
|
||||
this.bftPrivacyType = bftPrivacyType;
|
||||
}
|
||||
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static Collection<BftPrivacyType> bftPrivacyTypes() {
|
||||
final List<BftPrivacyType> bftPrivacyTypes = new ArrayList<>();
|
||||
for (EnclaveType x : EnclaveType.valuesForTests()) {
|
||||
for (ConsensusType consensusType : ConsensusType.values()) {
|
||||
bftPrivacyTypes.add(
|
||||
new BftPrivacyType(
|
||||
x, EnclaveEncryptorType.NACL, consensusType, Restriction.RESTRICTED));
|
||||
bftPrivacyTypes.add(
|
||||
new BftPrivacyType(x, EnclaveEncryptorType.EC, consensusType, Restriction.RESTRICTED));
|
||||
}
|
||||
}
|
||||
|
||||
for (ConsensusType consensusType : ConsensusType.values()) {
|
||||
bftPrivacyTypes.add(
|
||||
new BftPrivacyType(
|
||||
EnclaveType.NOOP,
|
||||
EnclaveEncryptorType.NOOP,
|
||||
consensusType,
|
||||
Restriction.UNRESTRICTED));
|
||||
}
|
||||
|
||||
return bftPrivacyTypes;
|
||||
}
|
||||
|
||||
private PrivacyNode alice;
|
||||
private PrivacyNode bob;
|
||||
private PrivacyNode charlie;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
final Network containerNetwork = Network.newNetwork();
|
||||
|
||||
alice = createNode(containerNetwork, "node1", 0);
|
||||
bob = createNode(containerNetwork, "node2", 1);
|
||||
charlie = createNode(containerNetwork, "node3", 2);
|
||||
|
||||
privacyCluster.start(alice, bob, charlie);
|
||||
alice.verify(priv.syncingStatus(false));
|
||||
bob.verify(priv.syncingStatus(false));
|
||||
charlie.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
private PrivacyNode createNode(
|
||||
final Network containerNetwork, final String nodeName, final int privacyAccount)
|
||||
throws IOException {
|
||||
if (bftPrivacyType.consensusType == ConsensusType.IBFT2) {
|
||||
return privacyBesu.createIbft2NodePrivacyEnabled(
|
||||
nodeName,
|
||||
PrivacyAccountResolver.values()[privacyAccount].resolve(
|
||||
bftPrivacyType.enclaveEncryptorType),
|
||||
true,
|
||||
bftPrivacyType.enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
bftPrivacyType.restriction == Restriction.UNRESTRICTED,
|
||||
"0xAA");
|
||||
} else if (bftPrivacyType.consensusType == ConsensusType.QBFT) {
|
||||
return privacyBesu.createQbftNodePrivacyEnabled(
|
||||
nodeName,
|
||||
PrivacyAccountResolver.values()[privacyAccount].resolve(
|
||||
bftPrivacyType.enclaveEncryptorType),
|
||||
bftPrivacyType.enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
bftPrivacyType.restriction == Restriction.UNRESTRICTED,
|
||||
"0xAA");
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown consensus type " + bftPrivacyType.consensusType);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onlyAliceAndBobCanExecuteContract() {
|
||||
// Contract address is generated from sender address and transaction nonce
|
||||
final String contractAddress =
|
||||
EnclaveEncryptorType.EC.equals(bftPrivacyType.enclaveEncryptorType)
|
||||
? "0x3e5d325a03ad3ce5640502219833d30b89ce3ce1"
|
||||
: "0xebf56429e6500e84442467292183d4d621359838";
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(contractAddress, alice.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
|
||||
final String transactionHash =
|
||||
alice.execute(
|
||||
privateContractTransactions.callSmartContract(
|
||||
eventEmitter.getContractAddress(),
|
||||
eventEmitter.store(BigInteger.ONE).encodeFunctionCall(),
|
||||
alice.getTransactionSigningKey(),
|
||||
bftPrivacyType.restriction,
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey()));
|
||||
|
||||
final PrivateTransactionReceipt expectedReceipt =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(transactionHash));
|
||||
|
||||
bob.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
transactionHash, expectedReceipt));
|
||||
|
||||
if (bftPrivacyType.restriction != Restriction.UNRESTRICTED) {
|
||||
charlie.verify(privateTransactionVerifier.noPrivateTransactionReceipt(transactionHash));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aliceCanDeployMultipleTimesInSingleGroup() {
|
||||
final String firstDeployedAddress =
|
||||
EnclaveEncryptorType.EC.equals(bftPrivacyType.enclaveEncryptorType)
|
||||
? "0x3e5d325a03ad3ce5640502219833d30b89ce3ce1"
|
||||
: "0xebf56429e6500e84442467292183d4d621359838";
|
||||
|
||||
privacyCluster.stopNode(charlie);
|
||||
|
||||
final EventEmitter firstEventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(firstDeployedAddress, alice.getAddress().toString())
|
||||
.verify(firstEventEmitter);
|
||||
|
||||
final String secondDeployedAddress =
|
||||
EnclaveEncryptorType.EC.equals(bftPrivacyType.enclaveEncryptorType)
|
||||
? "0x5194e214fae257530710d18c868df7a295d9d53b"
|
||||
: "0x10f807f8a905da5bd319196da7523c6bd768690f";
|
||||
|
||||
final EventEmitter secondEventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(secondDeployedAddress, alice.getAddress().toString())
|
||||
.verify(secondEventEmitter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canInteractWithMultiplePrivacyGroups() {
|
||||
// alice deploys contract
|
||||
final String firstDeployedAddress =
|
||||
EnclaveEncryptorType.EC.equals(bftPrivacyType.enclaveEncryptorType)
|
||||
? "0x760359bc605b3848f5199829bde6b382d90fb8eb"
|
||||
: "0xff206d21150a8da5b83629d8a722f3135ed532b1";
|
||||
|
||||
final EventEmitter firstEventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey(),
|
||||
charlie.getEnclaveKey()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(firstDeployedAddress, alice.getAddress().toString())
|
||||
.verify(firstEventEmitter);
|
||||
|
||||
// charlie interacts with contract
|
||||
final String firstTransactionHash =
|
||||
charlie.execute(
|
||||
privateContractTransactions.callSmartContract(
|
||||
firstEventEmitter.getContractAddress(),
|
||||
firstEventEmitter.store(BigInteger.ONE).encodeFunctionCall(),
|
||||
charlie.getTransactionSigningKey(),
|
||||
bftPrivacyType.restriction,
|
||||
charlie.getEnclaveKey(),
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey()));
|
||||
|
||||
// alice gets receipt from charlie's interaction
|
||||
final PrivateTransactionReceipt aliceReceipt =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(firstTransactionHash));
|
||||
|
||||
// verify bob and charlie have access to the same receipt
|
||||
bob.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
firstTransactionHash, aliceReceipt));
|
||||
charlie.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
firstTransactionHash, aliceReceipt));
|
||||
|
||||
// alice deploys second contract
|
||||
final String secondDeployedAddress =
|
||||
EnclaveEncryptorType.EC.equals(bftPrivacyType.enclaveEncryptorType)
|
||||
? "0x3e5d325a03ad3ce5640502219833d30b89ce3ce1"
|
||||
: "0xebf56429e6500e84442467292183d4d621359838";
|
||||
|
||||
final EventEmitter secondEventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(secondDeployedAddress, alice.getAddress().toString())
|
||||
.verify(secondEventEmitter);
|
||||
|
||||
// bob interacts with contract
|
||||
final String secondTransactionHash =
|
||||
bob.execute(
|
||||
privateContractTransactions.callSmartContract(
|
||||
secondEventEmitter.getContractAddress(),
|
||||
secondEventEmitter.store(BigInteger.ONE).encodeFunctionCall(),
|
||||
bob.getTransactionSigningKey(),
|
||||
bftPrivacyType.restriction,
|
||||
bob.getEnclaveKey(),
|
||||
alice.getEnclaveKey()));
|
||||
|
||||
// alice gets receipt from bob's interaction
|
||||
final PrivateTransactionReceipt secondExpectedReceipt =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(secondTransactionHash));
|
||||
|
||||
bob.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
secondTransactionHash, secondExpectedReceipt));
|
||||
|
||||
// charlie cannot see the receipt
|
||||
if (bftPrivacyType.restriction != Restriction.UNRESTRICTED) {
|
||||
charlie.verify(privateTransactionVerifier.noPrivateTransactionReceipt(secondTransactionHash));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* 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.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.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.web3j.utils.Restriction;
|
||||
|
||||
public class DeployPrivateSmartContractAcceptanceTest extends ParameterizedEnclaveTestBase {
|
||||
|
||||
private final PrivacyNode minerNode;
|
||||
|
||||
public DeployPrivateSmartContractAcceptanceTest(
|
||||
final Restriction restriction,
|
||||
final EnclaveType enclaveType,
|
||||
final EnclaveEncryptorType enclaveEncryptorType)
|
||||
throws IOException {
|
||||
super(restriction, enclaveType, enclaveEncryptorType);
|
||||
|
||||
minerNode =
|
||||
privacyBesu.createPrivateTransactionEnabledMinerNode(
|
||||
restriction + "-node",
|
||||
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
|
||||
enclaveType,
|
||||
Optional.empty(),
|
||||
false,
|
||||
false,
|
||||
restriction == UNRESTRICTED);
|
||||
|
||||
privacyCluster.start(minerNode);
|
||||
minerNode.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deployingMustGiveValidReceiptAndCode() throws Exception {
|
||||
final String contractAddress =
|
||||
EnclaveEncryptorType.EC.equals(enclaveEncryptorType)
|
||||
? "0xfeeb2367e77e28f75fc3bcc55b70a535752db058"
|
||||
: "0x89ce396d0f9f937ddfa71113e29b2081c4869555";
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
minerNode.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
minerNode.getTransactionSigningKey(),
|
||||
minerNode.getEnclaveKey()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(contractAddress, minerNode.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
|
||||
privateContractVerifier.validContractCodeProvided().verify(eventEmitter);
|
||||
}
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
* 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.catchThrowable;
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveEncryptorType.EC;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveEncryptorType.NACL;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveType.TESSERA;
|
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyAcceptanceTestBase;
|
||||
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.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.crypto.sodium.Box;
|
||||
import org.assertj.core.api.Condition;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.testcontainers.containers.Network;
|
||||
import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
|
||||
import org.web3j.utils.Restriction;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class EnclaveErrorAcceptanceTest extends PrivacyAcceptanceTestBase {
|
||||
|
||||
private final PrivacyNode alice;
|
||||
private final PrivacyNode bob;
|
||||
private final String wrongPublicKey;
|
||||
|
||||
@Parameters(name = "{0} enclave type with {1} encryptor")
|
||||
public static Collection<Object[]> enclaveParameters() {
|
||||
return Arrays.asList(
|
||||
new Object[][] {
|
||||
{TESSERA, NACL},
|
||||
{TESSERA, EC}
|
||||
});
|
||||
}
|
||||
|
||||
public EnclaveErrorAcceptanceTest(
|
||||
final EnclaveType enclaveType, final EnclaveEncryptorType enclaveEncryptorType)
|
||||
throws IOException {
|
||||
|
||||
final Network containerNetwork = Network.newNetwork();
|
||||
|
||||
alice =
|
||||
privacyBesu.createIbft2NodePrivacyEnabled(
|
||||
"node1",
|
||||
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
|
||||
false,
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"0xAA");
|
||||
bob =
|
||||
privacyBesu.createIbft2NodePrivacyEnabled(
|
||||
"node2",
|
||||
PrivacyAccountResolver.BOB.resolve(enclaveEncryptorType),
|
||||
false,
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"0xBB");
|
||||
privacyCluster.start(alice, bob);
|
||||
|
||||
alice.verify(priv.syncingStatus(false));
|
||||
bob.verify(priv.syncingStatus(false));
|
||||
|
||||
final byte[] wrongPublicKeyBytes =
|
||||
EnclaveEncryptorType.EC.equals(enclaveEncryptorType)
|
||||
? getSECP256r1PublicKeyByteArray()
|
||||
: Box.KeyPair.random().publicKey().bytesArray();
|
||||
|
||||
wrongPublicKey = Base64.getEncoder().encodeToString(wrongPublicKeyBytes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aliceCannotSendTransactionFromBobNode() {
|
||||
final Throwable throwable =
|
||||
catchThrowable(
|
||||
() ->
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
wrongPublicKey,
|
||||
bob.getEnclaveKey())));
|
||||
|
||||
assertThat(throwable)
|
||||
.hasMessageContaining(
|
||||
RpcErrorType.PRIVATE_FROM_DOES_NOT_MATCH_ENCLAVE_PUBLIC_KEY.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enclaveNoPeerUrlError() {
|
||||
final Throwable throwable =
|
||||
catchThrowable(
|
||||
() ->
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
wrongPublicKey)));
|
||||
|
||||
final String tesseraMessage = RpcErrorType.TESSERA_NODE_MISSING_PEER_URL.getMessage();
|
||||
|
||||
assertThat(throwable.getMessage()).has(matchTesseraEnclaveMessage(tesseraMessage));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenEnclaveIsDisconnectedGetReceiptReturnsInternalError() {
|
||||
final EventEmitter eventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(
|
||||
eventEmitter.getContractAddress(), alice.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
|
||||
final String transactionHash =
|
||||
alice.execute(
|
||||
privateContractTransactions.callSmartContract(
|
||||
eventEmitter.getContractAddress(),
|
||||
eventEmitter.store(BigInteger.ONE).encodeFunctionCall(),
|
||||
alice.getTransactionSigningKey(),
|
||||
Restriction.RESTRICTED,
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey()));
|
||||
|
||||
final PrivateTransactionReceipt receiptBeforeEnclaveLosesConnection =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(transactionHash));
|
||||
|
||||
alice.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
transactionHash, receiptBeforeEnclaveLosesConnection));
|
||||
|
||||
alice.getEnclave().stop();
|
||||
|
||||
alice.verify(
|
||||
privateTransactionVerifier.internalErrorPrivateTransactionReceipt(transactionHash));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Web3J is broken by PR #1426")
|
||||
public void transactionFailsIfPartyIsOffline() {
|
||||
// Contract address is generated from sender address and transaction nonce
|
||||
final String contractAddress = "0xebf56429e6500e84442467292183d4d621359838";
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(contractAddress, alice.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
|
||||
bob.getEnclave().stop();
|
||||
|
||||
final Throwable throwable =
|
||||
catchThrowable(
|
||||
() ->
|
||||
alice.execute(
|
||||
privateContractTransactions.callSmartContract(
|
||||
eventEmitter.getContractAddress(),
|
||||
eventEmitter.store(BigInteger.ONE).encodeFunctionCall(),
|
||||
alice.getTransactionSigningKey(),
|
||||
Restriction.RESTRICTED,
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey())));
|
||||
|
||||
assertThat(throwable).hasMessageContaining("NodePropagatingToAllPeers");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPrivacyGroupReturnsCorrectError() {
|
||||
final Throwable throwable =
|
||||
catchThrowable(() -> alice.execute(privacyTransactions.createPrivacyGroup(null, null)));
|
||||
final String tesseraMessage = RpcErrorType.TESSERA_CREATE_GROUP_INCLUDE_SELF.getMessage();
|
||||
|
||||
assertThat(throwable.getMessage()).has(matchTesseraEnclaveMessage(tesseraMessage));
|
||||
}
|
||||
|
||||
private Condition<String> matchTesseraEnclaveMessage(final String enclaveMessage) {
|
||||
return new Condition<>(
|
||||
message -> message.contains(enclaveMessage),
|
||||
"Message did not match Tessera expected output");
|
||||
}
|
||||
|
||||
private byte[] getSECP256r1PublicKeyByteArray() {
|
||||
try {
|
||||
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
|
||||
final ECGenParameterSpec spec = new ECGenParameterSpec("secp256r1");
|
||||
keyGen.initialize(spec);
|
||||
return keyGen.generateKeyPair().getPublic().getEncoded();
|
||||
} catch (Exception exception) {
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,642 +0,0 @@
|
||||
/*
|
||||
* 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.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.hyperledger.besu.ethereum.core.PrivacyParameters.FLEXIBLE_PRIVACY_PROXY;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveEncryptorType.EC;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveEncryptorType.NACL;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveType.TESSERA;
|
||||
import static org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.eth.EthConditions;
|
||||
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.miner.MinerTransactions;
|
||||
import org.hyperledger.besu.tests.web3j.generated.EventEmitter;
|
||||
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.testcontainers.containers.Network;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
|
||||
import org.web3j.protocol.core.methods.response.EthCall;
|
||||
import org.web3j.protocol.core.methods.response.Log;
|
||||
import org.web3j.protocol.core.methods.response.TransactionReceipt;
|
||||
import org.web3j.tx.Contract;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class FlexiblePrivacyAcceptanceTest extends FlexiblePrivacyAcceptanceTestBase {
|
||||
|
||||
private final EnclaveType enclaveType;
|
||||
private final EnclaveEncryptorType enclaveEncryptorType;
|
||||
|
||||
public FlexiblePrivacyAcceptanceTest(
|
||||
final EnclaveType enclaveType, final EnclaveEncryptorType enclaveEncryptorType) {
|
||||
this.enclaveType = enclaveType;
|
||||
this.enclaveEncryptorType = enclaveEncryptorType;
|
||||
}
|
||||
|
||||
@Parameters(name = "{0} enclave type with {1} encryptor")
|
||||
public static Collection<Object[]> enclaveParameters() {
|
||||
return Arrays.asList(
|
||||
new Object[][] {
|
||||
{TESSERA, NACL},
|
||||
{TESSERA, EC}
|
||||
});
|
||||
}
|
||||
|
||||
private PrivacyNode alice;
|
||||
private PrivacyNode bob;
|
||||
private PrivacyNode charlie;
|
||||
|
||||
private final MinerTransactions minerTransactions = new MinerTransactions();
|
||||
private final EthConditions ethConditions = new EthConditions(ethTransactions);
|
||||
|
||||
private static final String EXPECTED_STORE_OUTPUT_DATA =
|
||||
"0x000000000000000000000000f17f52151ebef6c7334fad080c5704d77216b7320000000000000000000000000000000000000000000000000000000000000539";
|
||||
private static final String EXPECTED_STORE_EVENT_TOPIC =
|
||||
"0xc9db20adedc6cf2b5d25252b101ab03e124902a73fcb12b753f3d1aaa2d8f9f5";
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
final Network containerNetwork = Network.newNetwork();
|
||||
|
||||
alice =
|
||||
privacyBesu.createFlexiblePrivacyGroupEnabledMinerNode(
|
||||
"node1",
|
||||
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
|
||||
false,
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork));
|
||||
bob =
|
||||
privacyBesu.createFlexiblePrivacyGroupEnabledNode(
|
||||
"node2",
|
||||
PrivacyAccountResolver.BOB.resolve(enclaveEncryptorType),
|
||||
false,
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork));
|
||||
charlie =
|
||||
privacyBesu.createFlexiblePrivacyGroupEnabledNode(
|
||||
"node3",
|
||||
PrivacyAccountResolver.CHARLIE.resolve(enclaveEncryptorType),
|
||||
false,
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork));
|
||||
privacyCluster.start(alice, bob, charlie);
|
||||
|
||||
alice.verify(priv.syncingStatus(false));
|
||||
bob.verify(priv.syncingStatus(false));
|
||||
charlie.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nodeCanCreatePrivacyGroup() {
|
||||
final String privacyGroupId = createFlexiblePrivacyGroup(alice);
|
||||
checkFlexiblePrivacyGroupExists(privacyGroupId, alice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deployingMustGiveValidReceipt() {
|
||||
final String privacyGroupId = createFlexiblePrivacyGroup(alice, bob);
|
||||
final Contract eventEmitter = deployPrivateContract(EventEmitter.class, privacyGroupId, alice);
|
||||
final String commitmentHash = getContractDeploymentCommitmentHash(eventEmitter);
|
||||
|
||||
alice.verify(privateTransactionVerifier.existingPrivateTransactionReceipt(commitmentHash));
|
||||
bob.verify(privateTransactionVerifier.existingPrivateTransactionReceipt(commitmentHash));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canAddParticipantToGroup() {
|
||||
final String privacyGroupId = createFlexiblePrivacyGroup(alice, bob);
|
||||
final Contract eventEmitter = deployPrivateContract(EventEmitter.class, privacyGroupId, alice);
|
||||
final String commitmentHash = getContractDeploymentCommitmentHash(eventEmitter);
|
||||
|
||||
charlie.verify(privateTransactionVerifier.noPrivateTransactionReceipt(commitmentHash));
|
||||
|
||||
final Credentials aliceCredentials = Credentials.create(alice.getTransactionSigningKey());
|
||||
lockPrivacyGroup(privacyGroupId, alice, aliceCredentials);
|
||||
addMembersToPrivacyGroup(privacyGroupId, alice, aliceCredentials, charlie);
|
||||
checkFlexiblePrivacyGroupExists(privacyGroupId, alice, bob, charlie);
|
||||
|
||||
charlie.verify(privateTransactionVerifier.existingPrivateTransactionReceipt(commitmentHash));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removedMemberCannotSendTransactionToGroup() {
|
||||
final String privacyGroupId = createFlexiblePrivacyGroup(alice, bob);
|
||||
|
||||
final String removeHash =
|
||||
removeFromPrivacyGroup(
|
||||
privacyGroupId, alice, Credentials.create(alice.getTransactionSigningKey()), bob);
|
||||
|
||||
bob.verify(privateTransactionVerifier.existingPrivateTransactionReceipt(removeHash));
|
||||
|
||||
assertThatThrownBy(() -> deployPrivateContract(EventEmitter.class, privacyGroupId, bob))
|
||||
.isInstanceOf(RuntimeException.class)
|
||||
.hasMessageContaining("Flexible Privacy group does not exist.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canInteractWithPrivateGenesisPreCompile() throws Exception {
|
||||
final String privacyGroupId = createFlexiblePrivacyGroup(alice, bob);
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.loadSmartContractWithPrivacyGroupId(
|
||||
"0x1000000000000000000000000000000000000001",
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
privateTransactionVerifier.existingPrivateTransactionReceipt(
|
||||
eventEmitter.store(BigInteger.valueOf(42)).send().getTransactionHash());
|
||||
|
||||
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 = createFlexiblePrivacyGroup(alice);
|
||||
|
||||
checkFlexiblePrivacyGroupExists(privacyGroupId, alice);
|
||||
|
||||
lockPrivacyGroup(privacyGroupId, alice, Credentials.create(alice.getTransactionSigningKey()));
|
||||
addMembersToPrivacyGroup(
|
||||
privacyGroupId, alice, Credentials.create(alice.getTransactionSigningKey()), bob);
|
||||
|
||||
checkFlexiblePrivacyGroupExists(privacyGroupId, alice, bob);
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
deployPrivateContract(EventEmitter.class, privacyGroupId, alice);
|
||||
|
||||
final int valueSetWhileBobWasMember = 17;
|
||||
final PrivateTransactionReceipt receiptWhileBobMember =
|
||||
setEventEmitterValueAndCheck(
|
||||
Lists.newArrayList(alice, bob),
|
||||
privacyGroupId,
|
||||
eventEmitter,
|
||||
valueSetWhileBobWasMember);
|
||||
|
||||
removeFromPrivacyGroup(
|
||||
privacyGroupId, alice, Credentials.create(alice.getTransactionSigningKey()), bob);
|
||||
|
||||
checkFlexiblePrivacyGroupExists(privacyGroupId, alice);
|
||||
|
||||
final int valueSetWhileBobWas_NOT_aMember = 1337;
|
||||
final PrivateTransactionReceipt receiptWhileBobRemoved =
|
||||
setEventEmitterValueAndCheck(
|
||||
Lists.newArrayList(alice),
|
||||
privacyGroupId,
|
||||
eventEmitter,
|
||||
valueSetWhileBobWas_NOT_aMember);
|
||||
checkEmitterValue(
|
||||
Lists.newArrayList(bob),
|
||||
privacyGroupId,
|
||||
eventEmitter,
|
||||
valueSetWhileBobWasMember); // bob did not get the last transaction
|
||||
|
||||
lockPrivacyGroup(privacyGroupId, alice, Credentials.create(alice.getTransactionSigningKey()));
|
||||
addMembersToPrivacyGroup(
|
||||
privacyGroupId, alice, Credentials.create(alice.getTransactionSigningKey()), bob);
|
||||
|
||||
checkFlexiblePrivacyGroupExists(privacyGroupId, alice, bob);
|
||||
|
||||
checkEmitterValue(
|
||||
Lists.newArrayList(alice, bob),
|
||||
privacyGroupId,
|
||||
eventEmitter,
|
||||
valueSetWhileBobWas_NOT_aMember);
|
||||
|
||||
PrivateTransactionReceipt receipt =
|
||||
bob.execute(
|
||||
privacyTransactions.getPrivateTransactionReceipt(
|
||||
receiptWhileBobRemoved.getcommitmentHash()));
|
||||
assertThat(receipt.getStatus()).isEqualTo("0x1");
|
||||
|
||||
receipt =
|
||||
bob.execute(
|
||||
privacyTransactions.getPrivateTransactionReceipt(
|
||||
receiptWhileBobMember.getcommitmentHash()));
|
||||
assertThat(receipt.getStatus()).isEqualTo("0x1");
|
||||
}
|
||||
|
||||
PrivateTransactionReceipt setEventEmitterValueAndCheck(
|
||||
final List<PrivacyNode> nodes,
|
||||
final String privacyGroupId,
|
||||
final EventEmitter eventEmitter,
|
||||
final int value) {
|
||||
final PrivateTransactionReceipt receiptWhileBobMember =
|
||||
setEventEmitterValue(nodes, privacyGroupId, eventEmitter, value);
|
||||
|
||||
checkEmitterValue(nodes, privacyGroupId, eventEmitter, value);
|
||||
return receiptWhileBobMember;
|
||||
}
|
||||
|
||||
private PrivateTransactionReceipt setEventEmitterValue(
|
||||
final List<PrivacyNode> nodes,
|
||||
final String privacyGroupId,
|
||||
final EventEmitter eventEmitter,
|
||||
final int value) {
|
||||
final PrivacyNode firstNode = nodes.get(0);
|
||||
final String txHash =
|
||||
firstNode.execute(
|
||||
privateContractTransactions.callOnchainPermissioningSmartContract(
|
||||
eventEmitter.getContractAddress(),
|
||||
eventEmitter.store(BigInteger.valueOf(value)).encodeFunctionCall(),
|
||||
firstNode.getTransactionSigningKey(),
|
||||
firstNode.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
PrivateTransactionReceipt receipt = null;
|
||||
for (final PrivacyNode node : nodes) {
|
||||
receipt = node.execute(privacyTransactions.getPrivateTransactionReceipt(txHash));
|
||||
assertThat(receipt.getStatus()).isEqualTo("0x1");
|
||||
}
|
||||
return receipt;
|
||||
}
|
||||
|
||||
private void checkEmitterValue(
|
||||
final List<PrivacyNode> nodes,
|
||||
final String privacyGroupId,
|
||||
final EventEmitter eventEmitter,
|
||||
final int expectedValue) {
|
||||
for (final PrivacyNode node : nodes) {
|
||||
final EthCall response =
|
||||
node.execute(
|
||||
privacyTransactions.privCall(
|
||||
privacyGroupId, eventEmitter, eventEmitter.value().encodeFunctionCall()));
|
||||
|
||||
assertThat(new BigInteger(response.getValue().substring(2), 16))
|
||||
.isEqualByComparingTo(BigInteger.valueOf(expectedValue));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bobCanAddCharlieAfterBeingAddedByAlice() {
|
||||
final String privacyGroupId = createFlexiblePrivacyGroup(alice);
|
||||
checkFlexiblePrivacyGroupExists(privacyGroupId, alice);
|
||||
final EventEmitter eventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(
|
||||
eventEmitter.getContractAddress(), alice.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
|
||||
final Credentials aliceCredentials = Credentials.create(alice.getTransactionSigningKey());
|
||||
final String aliceLockHash =
|
||||
alice.execute(
|
||||
privacyTransactions.privxLockPrivacyGroupAndCheck(
|
||||
privacyGroupId, alice, aliceCredentials));
|
||||
|
||||
alice.execute(
|
||||
privacyTransactions.addToPrivacyGroup(privacyGroupId, alice, aliceCredentials, bob));
|
||||
|
||||
checkFlexiblePrivacyGroupExists(privacyGroupId, alice, bob);
|
||||
|
||||
bob.execute(
|
||||
privacyTransactions.privxLockPrivacyGroupAndCheck(privacyGroupId, bob, aliceCredentials));
|
||||
|
||||
alice.execute(minerTransactions.minerStop());
|
||||
|
||||
alice.getBesu().verify(ethConditions.miningStatus(false));
|
||||
|
||||
final BigInteger pendingTransactionFilterId =
|
||||
alice.execute(ethTransactions.newPendingTransactionsFilter());
|
||||
|
||||
final String callHash =
|
||||
alice.execute(
|
||||
privateContractTransactions.callOnchainPermissioningSmartContract(
|
||||
eventEmitter.getContractAddress(),
|
||||
eventEmitter.value().encodeFunctionCall(),
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
final String bobAddHash =
|
||||
addMembersToPrivacyGroup(privacyGroupId, bob, aliceCredentials, charlie);
|
||||
|
||||
alice
|
||||
.getBesu()
|
||||
.verify(
|
||||
ethConditions.expectNewPendingTransactions(
|
||||
pendingTransactionFilterId, Arrays.asList(callHash, bobAddHash)));
|
||||
|
||||
alice.execute(minerTransactions.minerStart());
|
||||
|
||||
alice.getBesu().verify(ethConditions.miningStatus(true));
|
||||
|
||||
checkFlexiblePrivacyGroupExists(privacyGroupId, alice, bob, charlie);
|
||||
|
||||
final Optional<TransactionReceipt> aliceAddReceipt =
|
||||
alice.execute(ethTransactions.getTransactionReceipt(bobAddHash));
|
||||
assertThat(aliceAddReceipt.get().getStatus())
|
||||
.isEqualTo("0x1"); // this means the PMT for the "add" succeeded which is what we expect
|
||||
|
||||
final Optional<TransactionReceipt> alicePublicReceipt =
|
||||
alice.execute(ethTransactions.getTransactionReceipt(callHash));
|
||||
if (alicePublicReceipt.isPresent()) {
|
||||
assertThat(alicePublicReceipt.get().getBlockHash())
|
||||
.isEqualTo(
|
||||
aliceAddReceipt
|
||||
.get()
|
||||
.getBlockHash()); // ensure that "add" and "call" are in the same block
|
||||
assertThat(alicePublicReceipt.get().getStatus())
|
||||
.isEqualTo(
|
||||
"0x1"); // this means the PMT for the "call" succeeded which is what we expect because
|
||||
// it is in the same block as the "add" and there is no way to tell that this
|
||||
// will happen before the block is mined
|
||||
}
|
||||
|
||||
final PrivateTransactionReceipt aliceReceipt =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(callHash));
|
||||
assertThat(aliceReceipt.getStatus())
|
||||
.isEqualTo(
|
||||
"0x0"); // this means the "call" failed which is what we expect because the group was
|
||||
// locked!
|
||||
final PrivateTransactionReceipt bobReceipt =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(callHash));
|
||||
assertThat(bobReceipt.getStatus())
|
||||
.isEqualTo(
|
||||
"0x0"); // this means the "call" failed which is what we expect because the group was
|
||||
// locked!
|
||||
|
||||
// assert charlie can access private transaction information from before he was added
|
||||
final PrivateTransactionReceipt expectedAliceLockReceipt =
|
||||
new PrivateTransactionReceipt(
|
||||
null,
|
||||
alice.getAddress().toHexString(),
|
||||
FLEXIBLE_PRIVACY_PROXY.toHexString(),
|
||||
"0x",
|
||||
Collections.emptyList(),
|
||||
null,
|
||||
null,
|
||||
alice.getEnclaveKey(),
|
||||
null,
|
||||
privacyGroupId,
|
||||
"0x1",
|
||||
null);
|
||||
charlie.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
aliceLockHash, expectedAliceLockReceipt));
|
||||
|
||||
final String aliceStoreHash =
|
||||
charlie.execute(
|
||||
privateContractTransactions.callOnchainPermissioningSmartContract(
|
||||
eventEmitter.getContractAddress(),
|
||||
eventEmitter.store(BigInteger.valueOf(1337)).encodeFunctionCall(),
|
||||
charlie.getTransactionSigningKey(),
|
||||
charlie.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
final PrivateTransactionReceipt expectedStoreReceipt =
|
||||
new PrivateTransactionReceipt(
|
||||
null,
|
||||
charlie.getAddress().toHexString(),
|
||||
eventEmitter.getContractAddress(),
|
||||
"0x",
|
||||
Collections.singletonList(
|
||||
new Log(
|
||||
false,
|
||||
"0x0",
|
||||
"0x0",
|
||||
aliceStoreHash,
|
||||
null,
|
||||
null,
|
||||
eventEmitter.getContractAddress(),
|
||||
EXPECTED_STORE_OUTPUT_DATA,
|
||||
null,
|
||||
Collections.singletonList(EXPECTED_STORE_EVENT_TOPIC))),
|
||||
null,
|
||||
null,
|
||||
charlie.getEnclaveKey(),
|
||||
null,
|
||||
privacyGroupId,
|
||||
"0x1",
|
||||
null);
|
||||
|
||||
alice.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
aliceStoreHash, expectedStoreReceipt));
|
||||
|
||||
bob.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
aliceStoreHash, expectedStoreReceipt));
|
||||
|
||||
charlie.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
aliceStoreHash, expectedStoreReceipt));
|
||||
|
||||
removeFromPrivacyGroup(privacyGroupId, bob, aliceCredentials, charlie);
|
||||
|
||||
checkFlexiblePrivacyGroupExists(privacyGroupId, alice, bob);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canOnlyCallProxyContractWhenGroupLocked() {
|
||||
final String privacyGroupId = createFlexiblePrivacyGroup(alice);
|
||||
checkFlexiblePrivacyGroupExists(privacyGroupId, alice);
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(
|
||||
eventEmitter.getContractAddress(), alice.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
|
||||
final Credentials aliceCredentials = Credentials.create(alice.getTransactionSigningKey());
|
||||
|
||||
final Supplier<String> callContract =
|
||||
() -> {
|
||||
return alice.execute(
|
||||
privateContractTransactions.callOnchainPermissioningSmartContract(
|
||||
eventEmitter.getContractAddress(),
|
||||
eventEmitter.value().encodeFunctionCall(),
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
};
|
||||
|
||||
final String lockHash =
|
||||
alice.execute(
|
||||
privacyTransactions.privxLockPrivacyGroupAndCheck(
|
||||
privacyGroupId, alice, aliceCredentials));
|
||||
|
||||
final String callWhileLockedHash = callContract.get();
|
||||
|
||||
final String unlockHash =
|
||||
alice.execute(
|
||||
privacyTransactions.privxUnlockPrivacyGroupAndCheck(
|
||||
privacyGroupId, alice, aliceCredentials));
|
||||
|
||||
final String callAfterUnlockedHash = callContract.get();
|
||||
|
||||
alice.execute(minerTransactions.minerStart());
|
||||
alice.getBesu().verify(ethConditions.miningStatus(true));
|
||||
|
||||
final BiConsumer<String, String> assertThatTransactionReceiptIs =
|
||||
(String hash, String expectedResult) -> {
|
||||
final PrivateTransactionReceipt receipt =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(hash));
|
||||
assertThat(receipt.getStatus()).isEqualTo(expectedResult);
|
||||
};
|
||||
|
||||
// when locking a group succeeds ...
|
||||
assertThatTransactionReceiptIs.accept(lockHash, "0x1");
|
||||
// ... calls to contracts fail ...
|
||||
assertThatTransactionReceiptIs.accept(callWhileLockedHash, "0x0");
|
||||
// ... but unlock the group works ...
|
||||
assertThatTransactionReceiptIs.accept(unlockHash, "0x1");
|
||||
// ... and afterwards we can call contracts again
|
||||
assertThatTransactionReceiptIs.accept(callAfterUnlockedHash, "0x1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addMembersToTwoGroupsInTheSameBlock() {
|
||||
final String privacyGroupId1 = createFlexiblePrivacyGroup(alice);
|
||||
final String privacyGroupId2 = createFlexiblePrivacyGroup(bob);
|
||||
checkFlexiblePrivacyGroupExists(privacyGroupId1, alice);
|
||||
checkFlexiblePrivacyGroupExists(privacyGroupId2, bob);
|
||||
|
||||
lockPrivacyGroup(privacyGroupId1, alice, Credentials.create(alice.getTransactionSigningKey()));
|
||||
lockPrivacyGroup(privacyGroupId2, bob, Credentials.create(bob.getTransactionSigningKey()));
|
||||
|
||||
final BigInteger pendingTransactionFilterId =
|
||||
alice.execute(ethTransactions.newPendingTransactionsFilter());
|
||||
|
||||
alice.execute(minerTransactions.minerStop());
|
||||
alice.getBesu().verify(ethConditions.miningStatus(false));
|
||||
|
||||
final String aliceAddHash =
|
||||
addMembersToPrivacyGroup(
|
||||
privacyGroupId1, alice, Credentials.create(alice.getTransactionSigningKey()), charlie);
|
||||
final String bobAddHash =
|
||||
addMembersToPrivacyGroup(
|
||||
privacyGroupId2, bob, Credentials.create(bob.getTransactionSigningKey()), alice);
|
||||
|
||||
alice
|
||||
.getBesu()
|
||||
.verify(
|
||||
ethConditions.expectNewPendingTransactions(
|
||||
pendingTransactionFilterId, Arrays.asList(aliceAddHash, bobAddHash)));
|
||||
|
||||
alice.execute(minerTransactions.minerStart());
|
||||
|
||||
checkFlexiblePrivacyGroupExists(privacyGroupId1, alice, charlie);
|
||||
checkFlexiblePrivacyGroupExists(privacyGroupId2, bob, alice);
|
||||
}
|
||||
|
||||
private <T extends Contract> T deployPrivateContract(
|
||||
final Class<T> clazz, final String privacyGroupId, final PrivacyNode sender) {
|
||||
final T contract =
|
||||
sender.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
clazz, sender.getTransactionSigningKey(), sender.getEnclaveKey(), privacyGroupId));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(contract.getContractAddress(), sender.getAddress().toString())
|
||||
.verify(contract);
|
||||
|
||||
return contract;
|
||||
}
|
||||
|
||||
private String addMembersToPrivacyGroup(
|
||||
final String privacyGroupId,
|
||||
final PrivacyNode nodeAddingMember,
|
||||
final Credentials signer,
|
||||
final PrivacyNode... newMembers) {
|
||||
return nodeAddingMember.execute(
|
||||
privacyTransactions.addToPrivacyGroup(
|
||||
privacyGroupId, nodeAddingMember, signer, newMembers));
|
||||
}
|
||||
|
||||
private String removeFromPrivacyGroup(
|
||||
final String privacyGroupId,
|
||||
final PrivacyNode nodeRemovingMember,
|
||||
final Credentials signer,
|
||||
final PrivacyNode memberBeingRemoved) {
|
||||
return nodeRemovingMember.execute(
|
||||
privacyTransactions.removeFromPrivacyGroup(
|
||||
privacyGroupId,
|
||||
nodeRemovingMember.getEnclaveKey(),
|
||||
signer,
|
||||
memberBeingRemoved.getEnclaveKey()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will send a transaction to lock the privacy group identified by the specified id.
|
||||
* This also checks if the lock transaction was successful.
|
||||
*
|
||||
* @return the hash of the lock privacy group transaction
|
||||
*/
|
||||
private String lockPrivacyGroup(
|
||||
final String privacyGroupId, final PrivacyNode member, final Credentials signer) {
|
||||
return member.execute(
|
||||
privacyTransactions.privxLockPrivacyGroupAndCheck(privacyGroupId, member, signer));
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
/*
|
||||
* 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.ethereum.core.PrivacyParameters.FLEXIBLE_PRIVACY_PROXY;
|
||||
import static org.hyperledger.besu.ethereum.privacy.group.FlexibleGroupManagement.GET_PARTICIPANTS_METHOD_SIGNATURE;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyAcceptanceTestBase;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyNode;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.condition.ExpectValidFlexiblePrivacyGroupCreated;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.transaction.CreateFlexiblePrivacyGroupTransaction;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyRequestFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.web3j.abi.FunctionEncoder;
|
||||
import org.web3j.abi.Utils;
|
||||
import org.web3j.abi.datatypes.DynamicArray;
|
||||
import org.web3j.abi.datatypes.DynamicBytes;
|
||||
import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
|
||||
import org.web3j.protocol.core.methods.response.TransactionReceipt;
|
||||
import org.web3j.tx.Contract;
|
||||
import org.web3j.utils.Base64String;
|
||||
|
||||
public class FlexiblePrivacyAcceptanceTestBase extends PrivacyAcceptanceTestBase {
|
||||
|
||||
protected String createFlexiblePrivacyGroup(final PrivacyNode... members) {
|
||||
final List<String> addresses =
|
||||
Arrays.stream(members).map(PrivacyNode::getEnclaveKey).collect(Collectors.toList());
|
||||
return createFlexiblePrivacyGroup(members[0].getEnclaveKey(), addresses, members);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an flexible 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.
|
||||
*
|
||||
* @param members the list of members of the privacy group. The first member of the list will be
|
||||
* the creator of the group.
|
||||
* @return the id of the privacy group
|
||||
*/
|
||||
protected String createFlexiblePrivacyGroup(
|
||||
final String privateFrom, final List<String> addresses, final PrivacyNode... members) {
|
||||
|
||||
final PrivacyNode groupCreator = members[0];
|
||||
|
||||
final CreateFlexiblePrivacyGroupTransaction createTx =
|
||||
privacyTransactions.createFlexiblePrivacyGroup(groupCreator, privateFrom, addresses);
|
||||
|
||||
final PrivacyRequestFactory.PrivxCreatePrivacyGroupResponse createResponse =
|
||||
groupCreator.execute(createTx);
|
||||
final String privacyGroupId = createResponse.getPrivacyGroupId();
|
||||
|
||||
final List<Base64String> membersEnclaveKeys =
|
||||
Arrays.stream(members)
|
||||
.map(m -> Base64String.wrap(m.getEnclaveKey()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (final PrivacyNode member : members) {
|
||||
member.verify(flexiblePrivacyGroupExists(privacyGroupId, membersEnclaveKeys));
|
||||
}
|
||||
|
||||
final String commitmentHash =
|
||||
callGetParticipantsMethodAndReturnCommitmentHash(privacyGroupId, groupCreator, privateFrom);
|
||||
final PrivateTransactionReceipt expectedReceipt =
|
||||
buildExpectedAddMemberTransactionReceipt(privacyGroupId, groupCreator, addresses);
|
||||
|
||||
for (final PrivacyNode member : members) {
|
||||
member.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
commitmentHash, expectedReceipt));
|
||||
}
|
||||
|
||||
return privacyGroupId;
|
||||
}
|
||||
|
||||
protected String callGetParticipantsMethodAndReturnCommitmentHash(
|
||||
final String privacyGroupId, final PrivacyNode groupCreator, final String privateFrom) {
|
||||
return groupCreator.execute(
|
||||
privateContractTransactions.callOnchainPermissioningSmartContract(
|
||||
FLEXIBLE_PRIVACY_PROXY.toHexString(),
|
||||
GET_PARTICIPANTS_METHOD_SIGNATURE.toString(),
|
||||
groupCreator.getTransactionSigningKey(),
|
||||
privateFrom,
|
||||
privacyGroupId));
|
||||
}
|
||||
|
||||
protected PrivateTransactionReceipt buildExpectedAddMemberTransactionReceipt(
|
||||
final String privacyGroupId, final PrivacyNode groupCreator, final List<String> members) {
|
||||
return buildExpectedAddMemberTransactionReceipt(
|
||||
privacyGroupId, groupCreator, groupCreator.getEnclaveKey(), members);
|
||||
}
|
||||
|
||||
protected PrivateTransactionReceipt buildExpectedAddMemberTransactionReceipt(
|
||||
final String privacyGroupId,
|
||||
final PrivacyNode groupCreator,
|
||||
final String privateFrom,
|
||||
final List<String> members) {
|
||||
|
||||
final StringBuilder output = new StringBuilder();
|
||||
// hex prefix
|
||||
output.append("0x");
|
||||
|
||||
final String encodedParameters =
|
||||
FunctionEncoder.encode(
|
||||
"",
|
||||
Arrays.asList(
|
||||
new DynamicArray<>(
|
||||
DynamicBytes.class,
|
||||
Utils.typeMap(
|
||||
members.stream()
|
||||
.map(Bytes::fromBase64String)
|
||||
.map(Bytes::toArrayUnsafe)
|
||||
.collect(Collectors.toList()),
|
||||
DynamicBytes.class))));
|
||||
|
||||
output.append(encodedParameters);
|
||||
|
||||
return new PrivateTransactionReceipt(
|
||||
null,
|
||||
groupCreator.getAddress().toHexString(),
|
||||
FLEXIBLE_PRIVACY_PROXY.toHexString(),
|
||||
output.toString(),
|
||||
Collections.emptyList(),
|
||||
null,
|
||||
null,
|
||||
privateFrom,
|
||||
null,
|
||||
privacyGroupId,
|
||||
"0x1",
|
||||
null);
|
||||
}
|
||||
|
||||
protected ExpectValidFlexiblePrivacyGroupCreated flexiblePrivacyGroupExists(
|
||||
final String privacyGroupId, final List<Base64String> members) {
|
||||
return privateTransactionVerifier.flexiblePrivacyGroupExists(privacyGroupId, members);
|
||||
}
|
||||
|
||||
protected String getContractDeploymentCommitmentHash(final Contract contract) {
|
||||
final Optional<TransactionReceipt> transactionReceipt = contract.getTransactionReceipt();
|
||||
assertThat(transactionReceipt).isPresent();
|
||||
final PrivateTransactionReceipt privateTransactionReceipt =
|
||||
(PrivateTransactionReceipt) transactionReceipt.get();
|
||||
return privateTransactionReceipt.getcommitmentHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will check if a privacy group with the specified id and list of members exists.
|
||||
* Each one of the members node will be queried to ensure that they all have the same privacy
|
||||
* group in their private state.
|
||||
*
|
||||
* @param privacyGroupId the id of the privacy group
|
||||
* @param members the list of member in the privacy group
|
||||
*/
|
||||
protected void checkFlexiblePrivacyGroupExists(
|
||||
final String privacyGroupId, final PrivacyNode... members) {
|
||||
final List<Base64String> membersEnclaveKeys =
|
||||
Arrays.stream(members)
|
||||
.map(PrivacyNode::getEnclaveKey)
|
||||
.map(Base64String::wrap)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (final PrivacyNode member : members) {
|
||||
member.verify(flexiblePrivacyGroupExists(privacyGroupId, membersEnclaveKeys));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* 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 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.acceptance.dsl.privacy.account.PrivacyAccount;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.account.PrivacyAccountResolver;
|
||||
import org.hyperledger.besu.tests.web3j.generated.EventEmitter;
|
||||
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveKeyConfiguration;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.web3j.protocol.core.DefaultBlockParameter;
|
||||
import org.web3j.protocol.core.methods.response.EthBlock.Block;
|
||||
import org.web3j.protocol.core.methods.response.TransactionReceipt;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class PluginPrivacySigningAcceptanceTest extends PrivacyAcceptanceTestBase {
|
||||
private PrivacyNode minerNode;
|
||||
|
||||
private final EnclaveEncryptorType enclaveEncryptorType;
|
||||
|
||||
public PluginPrivacySigningAcceptanceTest(final EnclaveEncryptorType enclaveEncryptorType) {
|
||||
this.enclaveEncryptorType = enclaveEncryptorType;
|
||||
}
|
||||
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static Collection<EnclaveEncryptorType> enclaveEncryptorTypes() {
|
||||
return Arrays.stream(EnclaveEncryptorType.values())
|
||||
.filter(encryptorType -> !EnclaveEncryptorType.NOOP.equals(encryptorType))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
final PrivacyAccount BOB = PrivacyAccountResolver.BOB.resolve(enclaveEncryptorType);
|
||||
|
||||
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(),
|
||||
BOB.getEnclaveEncryptorType())),
|
||||
EnclaveType.NOOP,
|
||||
Optional.empty());
|
||||
|
||||
privacyCluster.start(minerNode);
|
||||
|
||||
minerNode.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canDeployContractSignedByPlugin() throws Exception {
|
||||
final String contractAddress =
|
||||
EnclaveEncryptorType.EC.equals(enclaveEncryptorType)
|
||||
? "0xf01ec73d91fdeb8bb9388ec74e6a3981da86e021"
|
||||
: "0xd0152772c54cecfa7684f09f7616dcc825545dff";
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
minerNode.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
minerNode.getTransactionSigningKey(),
|
||||
minerNode.getEnclaveKey()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(contractAddress, minerNode.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
privateContractVerifier.validContractCodeProvided().verify(eventEmitter);
|
||||
|
||||
final BigInteger blockNumberContractDeployed =
|
||||
eventEmitter.getTransactionReceipt().get().getBlockNumber();
|
||||
final Block blockContractDeployed =
|
||||
minerNode.execute(
|
||||
ethTransactions.block(DefaultBlockParameter.valueOf(blockNumberContractDeployed)));
|
||||
|
||||
assertThat(blockContractDeployed.getTransactions().size()).isEqualTo(1);
|
||||
|
||||
final String transactionHashContractDeployed =
|
||||
(String) blockContractDeployed.getTransactions().get(0).get();
|
||||
final TransactionReceipt pmtReceipt =
|
||||
minerNode
|
||||
.execute(ethTransactions.getTransactionReceipt(transactionHashContractDeployed))
|
||||
.get();
|
||||
|
||||
assertThat(pmtReceipt.getStatus()).isEqualTo("0x1");
|
||||
assertThat(pmtReceipt.getFrom()).isEqualTo("0xfe3b557e8fb62b89f4916b721be55ceb828dbd73");
|
||||
}
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
/*
|
||||
* 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.web3j.generated.RevertReason.FUNC_REVERTWITHREVERTREASON;
|
||||
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.besu.tests.web3j.generated.RevertReason;
|
||||
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.junit.Test;
|
||||
import org.web3j.abi.FunctionEncoder;
|
||||
import org.web3j.abi.TypeReference;
|
||||
import org.web3j.abi.datatypes.Bool;
|
||||
import org.web3j.abi.datatypes.Function;
|
||||
import org.web3j.abi.datatypes.Type;
|
||||
import org.web3j.abi.datatypes.generated.Uint256;
|
||||
import org.web3j.protocol.core.Request;
|
||||
import org.web3j.protocol.core.methods.request.Transaction;
|
||||
import org.web3j.protocol.core.methods.response.EthCall;
|
||||
import org.web3j.protocol.core.methods.response.TransactionReceipt;
|
||||
import org.web3j.protocol.http.HttpService;
|
||||
import org.web3j.tx.Contract;
|
||||
import org.web3j.utils.Restriction;
|
||||
|
||||
public class PrivCallAcceptanceTest extends ParameterizedEnclaveTestBase {
|
||||
|
||||
private static final int VALUE = 1024;
|
||||
|
||||
private final PrivacyNode minerNode;
|
||||
|
||||
public PrivCallAcceptanceTest(
|
||||
final Restriction restriction,
|
||||
final EnclaveType enclaveType,
|
||||
final EnclaveEncryptorType enclaveEncryptorType)
|
||||
throws IOException {
|
||||
|
||||
super(restriction, enclaveType, enclaveEncryptorType);
|
||||
|
||||
minerNode =
|
||||
privacyBesu.createPrivateTransactionEnabledMinerNode(
|
||||
restriction + "-node",
|
||||
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
|
||||
enclaveType,
|
||||
Optional.empty(),
|
||||
false,
|
||||
false,
|
||||
restriction == UNRESTRICTED);
|
||||
|
||||
privacyCluster.start(minerNode);
|
||||
minerNode.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mustReturnCorrectValue() throws Exception {
|
||||
|
||||
final String privacyGroupId =
|
||||
minerNode.execute(createPrivacyGroup("myGroupName", "my group description", minerNode));
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
minerNode.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
minerNode.getTransactionSigningKey(),
|
||||
restriction,
|
||||
minerNode.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(
|
||||
eventEmitter.getContractAddress(), minerNode.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
|
||||
final Request<Object, EthCall> priv_call =
|
||||
privCall(privacyGroupId, eventEmitter, false, false, false);
|
||||
|
||||
EthCall resp = priv_call.send();
|
||||
|
||||
String value = resp.getValue();
|
||||
assertThat(new BigInteger(value.substring(2), 16)).isEqualByComparingTo(BigInteger.ZERO);
|
||||
|
||||
final TransactionReceipt receipt = eventEmitter.store(BigInteger.valueOf(VALUE)).send();
|
||||
assertThat(receipt).isNotNull();
|
||||
|
||||
resp = priv_call.send();
|
||||
value = resp.getValue();
|
||||
assertThat(new BigInteger(value.substring(2), 16))
|
||||
.isEqualByComparingTo(BigInteger.valueOf(VALUE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mustRevertWithRevertReason() throws Exception {
|
||||
|
||||
final String privacyGroupId =
|
||||
minerNode.execute(createPrivacyGroup("myGroupName", "my group description", minerNode));
|
||||
|
||||
final RevertReason revertReasonContract =
|
||||
minerNode.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
RevertReason.class,
|
||||
minerNode.getTransactionSigningKey(),
|
||||
restriction,
|
||||
minerNode.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(
|
||||
revertReasonContract.getContractAddress(), minerNode.getAddress().toString())
|
||||
.verify(revertReasonContract);
|
||||
|
||||
final Request<Object, EthCall> priv_call =
|
||||
privCall(privacyGroupId, revertReasonContract, false, false, true);
|
||||
|
||||
EthCall resp = priv_call.send();
|
||||
assertThat(resp.getRevertReason()).isEqualTo("Execution reverted: RevertReason");
|
||||
|
||||
byte[] bytes = Hex.decode(resp.getError().getData().substring(3, 203));
|
||||
String revertMessage =
|
||||
new String(Arrays.copyOfRange(bytes, 4, bytes.length), Charset.defaultCharset()).trim();
|
||||
assertThat(revertMessage).isEqualTo("RevertReason");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnEmptyResultWithNonExistingPrivacyGroup() throws IOException {
|
||||
|
||||
final String privacyGroupId =
|
||||
minerNode.execute(createPrivacyGroup("myGroupName", "my group description", minerNode));
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
minerNode.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
minerNode.getTransactionSigningKey(),
|
||||
restriction,
|
||||
minerNode.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(
|
||||
eventEmitter.getContractAddress(), minerNode.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
|
||||
final String invalidPrivacyGroup = constructInvalidString(privacyGroupId);
|
||||
final Request<Object, EthCall> privCall =
|
||||
privCall(invalidPrivacyGroup, eventEmitter, false, false, false);
|
||||
|
||||
final EthCall result = privCall.send();
|
||||
|
||||
assertThat(result.getResult()).isEqualTo("0x");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mustNotSucceedWithWronglyEncodedFunction() throws IOException {
|
||||
|
||||
final String privacyGroupId =
|
||||
minerNode.execute(createPrivacyGroup("myGroupName", "my group description", minerNode));
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
minerNode.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
minerNode.getTransactionSigningKey(),
|
||||
restriction,
|
||||
minerNode.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(
|
||||
eventEmitter.getContractAddress(), minerNode.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
|
||||
final Request<Object, EthCall> priv_call =
|
||||
privCall(privacyGroupId, eventEmitter, true, false, false);
|
||||
|
||||
final String errorMessage = priv_call.send().getError().getMessage();
|
||||
assertThat(errorMessage).isEqualTo("Private transaction failed");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mustReturn0xUsingInvalidContractAddress() throws IOException {
|
||||
|
||||
final String privacyGroupId =
|
||||
minerNode.execute(createPrivacyGroup("myGroupName", "my group description", minerNode));
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
minerNode.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
minerNode.getTransactionSigningKey(),
|
||||
restriction,
|
||||
minerNode.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(
|
||||
eventEmitter.getContractAddress(), minerNode.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
|
||||
final Request<Object, EthCall> priv_call =
|
||||
privCall(privacyGroupId, eventEmitter, false, true, false);
|
||||
|
||||
final EthCall result = priv_call.send();
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getResult()).isEqualTo("0x");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private String constructInvalidString(final String privacyGroupId) {
|
||||
final char[] chars = privacyGroupId.toCharArray();
|
||||
if (chars[3] == '0') {
|
||||
chars[3] = '1';
|
||||
} else {
|
||||
chars[3] = '0';
|
||||
}
|
||||
return String.valueOf(chars);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private Request<Object, EthCall> privCall(
|
||||
final String privacyGroupId,
|
||||
final Contract contract,
|
||||
final boolean useInvalidParameters,
|
||||
final boolean useInvalidContractAddress,
|
||||
final boolean useRevertFunction) {
|
||||
|
||||
final Uint256 invalid = new Uint256(BigInteger.TEN);
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
final List<Type> inputParameters =
|
||||
useInvalidParameters ? Arrays.asList(invalid) : Collections.emptyList();
|
||||
|
||||
final Function function =
|
||||
useRevertFunction
|
||||
? new Function(
|
||||
FUNC_REVERTWITHREVERTREASON,
|
||||
inputParameters,
|
||||
Arrays.<TypeReference<?>>asList(new TypeReference<Bool>() {}))
|
||||
: new Function(
|
||||
"value",
|
||||
inputParameters,
|
||||
Arrays.<TypeReference<?>>asList(new TypeReference<Uint256>() {}));
|
||||
|
||||
final String encoded = FunctionEncoder.encode(function);
|
||||
|
||||
final HttpService httpService =
|
||||
new HttpService(
|
||||
"http://"
|
||||
+ minerNode.getBesu().getHostName()
|
||||
+ ":"
|
||||
+ minerNode.getBesu().getJsonRpcPort().get());
|
||||
|
||||
final String validContractAddress = contract.getContractAddress();
|
||||
final String invalidContractAddress = constructInvalidString(validContractAddress);
|
||||
final String contractAddress =
|
||||
useInvalidContractAddress ? invalidContractAddress : validContractAddress;
|
||||
|
||||
final Transaction transaction =
|
||||
Transaction.createEthCallTransaction(null, contractAddress, encoded);
|
||||
|
||||
return new Request<>(
|
||||
"priv_call",
|
||||
Arrays.asList(privacyGroupId, transaction, "latest"),
|
||||
httpService,
|
||||
EthCall.class);
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
/*
|
||||
* 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 org.hyperledger.besu.datatypes.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.enclave.testutil.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.testcontainers.containers.Network;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class PrivDebugGetStateRootFlexibleGroupAcceptanceTest
|
||||
extends FlexiblePrivacyAcceptanceTestBase {
|
||||
|
||||
private final EnclaveType enclaveType;
|
||||
|
||||
public PrivDebugGetStateRootFlexibleGroupAcceptanceTest(final EnclaveType enclaveType) {
|
||||
this.enclaveType = enclaveType;
|
||||
}
|
||||
|
||||
@Parameters(name = "{0}")
|
||||
public static Collection<EnclaveType> enclaveTypes() {
|
||||
return EnclaveType.valuesForTests();
|
||||
}
|
||||
|
||||
private PrivacyNode aliceNode;
|
||||
private PrivacyNode bobNode;
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException, URISyntaxException {
|
||||
final Network containerNetwork = Network.newNetwork();
|
||||
|
||||
aliceNode =
|
||||
privacyBesu.createFlexiblePrivacyGroupEnabledMinerNode(
|
||||
"alice-node",
|
||||
PrivacyAccountResolver.ALICE.resolve(EnclaveEncryptorType.NACL),
|
||||
false,
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork));
|
||||
bobNode =
|
||||
privacyBesu.createFlexiblePrivacyGroupEnabledNode(
|
||||
"bob-node",
|
||||
PrivacyAccountResolver.BOB.resolve(EnclaveEncryptorType.NACL),
|
||||
false,
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork));
|
||||
|
||||
privacyCluster.start(aliceNode, bobNode);
|
||||
|
||||
aliceNode.verify(priv.syncingStatus(false));
|
||||
bobNode.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nodesInGroupShouldHaveSameStateRoot() {
|
||||
final String privacyGroupId = createFlexiblePrivacyGroup(aliceNode, bobNode);
|
||||
|
||||
final Hash aliceStateRootId =
|
||||
aliceNode
|
||||
.execute(privacyTransactions.debugGetStateRoot(privacyGroupId, "latest"))
|
||||
.getResult();
|
||||
|
||||
final Hash bobStateRootId =
|
||||
bobNode
|
||||
.execute(privacyTransactions.debugGetStateRoot(privacyGroupId, "latest"))
|
||||
.getResult();
|
||||
|
||||
assertThat(aliceStateRootId).isEqualTo(bobStateRootId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unknownGroupShouldReturnError() {
|
||||
final PrivacyRequestFactory.DebugGetStateRoot aliceResult =
|
||||
aliceNode.execute(
|
||||
privacyTransactions.debugGetStateRoot(
|
||||
Hash.wrap(Bytes32.random()).toBase64String(), "latest"));
|
||||
|
||||
assertThat(aliceResult.getResult()).isNull();
|
||||
assertThat(aliceResult.hasError()).isTrue();
|
||||
assertThat(aliceResult.getError()).isNotNull();
|
||||
assertThat(aliceResult.getError().getMessage()).contains("Error finding privacy group");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void blockParamShouldBeApplied() {
|
||||
waitForBlockHeight(aliceNode, 2);
|
||||
waitForBlockHeight(bobNode, 2);
|
||||
|
||||
final String privacyGroupId = createFlexiblePrivacyGroup(aliceNode, bobNode);
|
||||
|
||||
waitForBlockHeight(aliceNode, 10);
|
||||
waitForBlockHeight(bobNode, 10);
|
||||
|
||||
final Hash aliceResult1 =
|
||||
aliceNode.execute(privacyTransactions.debugGetStateRoot(privacyGroupId, "1")).getResult();
|
||||
final Hash bobResultInt1 =
|
||||
bobNode.execute(privacyTransactions.debugGetStateRoot(privacyGroupId, "1")).getResult();
|
||||
|
||||
assertThat(aliceResult1).isEqualTo(bobResultInt1);
|
||||
|
||||
final Hash aliceResultLatest =
|
||||
aliceNode
|
||||
.execute(privacyTransactions.debugGetStateRoot(privacyGroupId, "latest"))
|
||||
.getResult();
|
||||
|
||||
final Hash bobResultLatest =
|
||||
bobNode
|
||||
.execute(privacyTransactions.debugGetStateRoot(privacyGroupId, "latest"))
|
||||
.getResult();
|
||||
|
||||
assertThat(aliceResultLatest).isEqualTo(bobResultLatest);
|
||||
assertThat(aliceResult1).isNotEqualTo(aliceResultLatest);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canInteractWithPrivateGenesisPreCompile() throws Exception {
|
||||
final String privacyGroupId = createFlexiblePrivacyGroup(aliceNode, bobNode);
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
aliceNode.execute(
|
||||
privateContractTransactions.loadSmartContractWithPrivacyGroupId(
|
||||
"0x1000000000000000000000000000000000000001",
|
||||
EventEmitter.class,
|
||||
aliceNode.getTransactionSigningKey(),
|
||||
aliceNode.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
privateTransactionVerifier.existingPrivateTransactionReceipt(
|
||||
eventEmitter.store(BigInteger.valueOf(42)).send().getTransactionHash());
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* 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.UNRESTRICTED;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
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.acceptance.dsl.transaction.privacy.PrivacyRequestFactory;
|
||||
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.junit.Test;
|
||||
import org.testcontainers.containers.Network;
|
||||
import org.web3j.utils.Restriction;
|
||||
|
||||
public class PrivDebugGetStateRootOffchainGroupAcceptanceTest extends ParameterizedEnclaveTestBase {
|
||||
|
||||
private final PrivacyNode aliceNode;
|
||||
private final PrivacyNode bobNode;
|
||||
|
||||
public PrivDebugGetStateRootOffchainGroupAcceptanceTest(
|
||||
final Restriction restriction,
|
||||
final EnclaveType enclaveType,
|
||||
final EnclaveEncryptorType enclaveEncryptorType)
|
||||
throws IOException {
|
||||
|
||||
super(restriction, enclaveType, enclaveEncryptorType);
|
||||
|
||||
final Network containerNetwork = Network.newNetwork();
|
||||
|
||||
aliceNode =
|
||||
privacyBesu.createIbft2NodePrivacyEnabled(
|
||||
"alice-node",
|
||||
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
|
||||
false,
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
restriction == UNRESTRICTED,
|
||||
"0xAA");
|
||||
bobNode =
|
||||
privacyBesu.createIbft2NodePrivacyEnabled(
|
||||
"bob-node",
|
||||
PrivacyAccountResolver.BOB.resolve(enclaveEncryptorType),
|
||||
false,
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
restriction == UNRESTRICTED,
|
||||
"0xBB");
|
||||
|
||||
privacyCluster.start(aliceNode, bobNode);
|
||||
|
||||
aliceNode.verify(priv.syncingStatus(false));
|
||||
bobNode.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nodesInGroupShouldHaveSameStateRoot() {
|
||||
final String privacyGroupId =
|
||||
aliceNode.execute(
|
||||
createPrivacyGroup("testGroup", "A group for everyone", aliceNode, bobNode));
|
||||
|
||||
final Hash aliceStateRootId =
|
||||
aliceNode
|
||||
.execute(privacyTransactions.debugGetStateRoot(privacyGroupId, "latest"))
|
||||
.getResult();
|
||||
|
||||
final Hash bobStateRootId =
|
||||
bobNode
|
||||
.execute(privacyTransactions.debugGetStateRoot(privacyGroupId, "latest"))
|
||||
.getResult();
|
||||
|
||||
assertThat(aliceStateRootId).isEqualTo(bobStateRootId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unknownGroupShouldReturnError() {
|
||||
if (restriction != UNRESTRICTED) {
|
||||
final PrivacyRequestFactory.DebugGetStateRoot aliceResult =
|
||||
aliceNode.execute(
|
||||
privacyTransactions.debugGetStateRoot(
|
||||
Hash.wrap(Bytes32.random()).toBase64String(), "latest"));
|
||||
|
||||
assertThat(aliceResult.getResult()).isNull();
|
||||
assertThat(aliceResult.hasError()).isTrue();
|
||||
assertThat(aliceResult.getError()).isNotNull();
|
||||
assertThat(aliceResult.getError().getMessage()).contains("Error finding privacy group");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* 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.UNRESTRICTED;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
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.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.Test;
|
||||
import org.web3j.utils.Restriction;
|
||||
|
||||
public class PrivGetCodeAcceptanceTest extends ParameterizedEnclaveTestBase {
|
||||
|
||||
private final PrivacyNode alice;
|
||||
|
||||
public PrivGetCodeAcceptanceTest(
|
||||
final Restriction restriction,
|
||||
final EnclaveType enclaveType,
|
||||
final EnclaveEncryptorType enclaveEncryptorType)
|
||||
throws IOException {
|
||||
|
||||
super(restriction, enclaveType, enclaveEncryptorType);
|
||||
|
||||
alice =
|
||||
privacyBesu.createPrivateTransactionEnabledMinerNode(
|
||||
restriction + "-node",
|
||||
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
|
||||
enclaveType,
|
||||
Optional.empty(),
|
||||
false,
|
||||
false,
|
||||
restriction == UNRESTRICTED);
|
||||
|
||||
privacyCluster.start(alice);
|
||||
alice.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void privGetCodeReturnsDeployedContractBytecode() {
|
||||
final String privacyGroupId = createPrivacyGroup();
|
||||
final EventEmitter eventEmitterContract = deployPrivateContract(privacyGroupId);
|
||||
|
||||
final Bytes deployedContractCode =
|
||||
alice.execute(
|
||||
privacyTransactions.privGetCode(
|
||||
privacyGroupId,
|
||||
Address.fromHexString(eventEmitterContract.getContractAddress()),
|
||||
"latest"));
|
||||
|
||||
assertThat(eventEmitterContract.getContractBinary())
|
||||
.contains(deployedContractCode.toUnprefixedHexString());
|
||||
}
|
||||
|
||||
private EventEmitter deployPrivateContract(final String privacyGroupId) {
|
||||
final EventEmitter eventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
restriction,
|
||||
alice.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(
|
||||
eventEmitter.getContractAddress(), alice.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
return eventEmitter;
|
||||
}
|
||||
|
||||
private String createPrivacyGroup() {
|
||||
final String privacyGroupId =
|
||||
alice.execute(createPrivacyGroup("myGroupName", "my group description", alice));
|
||||
|
||||
assertThat(privacyGroupId).isNotNull();
|
||||
|
||||
return privacyGroupId;
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* 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.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.acceptance.dsl.privacy.util.LogFilterJsonParameter;
|
||||
import org.hyperledger.besu.tests.web3j.generated.EventEmitter;
|
||||
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
|
||||
import org.web3j.protocol.core.methods.response.EthLog.LogResult;
|
||||
import org.web3j.utils.Restriction;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class PrivGetLogsAcceptanceTest extends ParameterizedEnclaveTestBase {
|
||||
|
||||
/*
|
||||
This value is derived from the contract event signature
|
||||
*/
|
||||
private static final String EVENT_EMITTER_EVENT_TOPIC =
|
||||
"0xc9db20adedc6cf2b5d25252b101ab03e124902a73fcb12b753f3d1aaa2d8f9f5";
|
||||
|
||||
private final PrivacyNode node;
|
||||
|
||||
public PrivGetLogsAcceptanceTest(
|
||||
final Restriction restriction,
|
||||
final EnclaveType enclaveType,
|
||||
final EnclaveEncryptorType enclaveEncryptorType)
|
||||
throws IOException {
|
||||
|
||||
super(restriction, enclaveType, enclaveEncryptorType);
|
||||
|
||||
node =
|
||||
privacyBesu.createPrivateTransactionEnabledMinerNode(
|
||||
restriction + "-node",
|
||||
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
|
||||
enclaveType,
|
||||
Optional.empty(),
|
||||
false,
|
||||
false,
|
||||
restriction == UNRESTRICTED);
|
||||
|
||||
privacyCluster.start(node);
|
||||
node.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLogsUsingBlockRangeFilter() {
|
||||
final String privacyGroupId = createPrivacyGroup();
|
||||
final EventEmitter eventEmitterContract = deployEventEmitterContract(privacyGroupId);
|
||||
|
||||
/*
|
||||
Updating the contract value 2 times
|
||||
*/
|
||||
updateContractValue(privacyGroupId, eventEmitterContract, 1);
|
||||
updateContractValue(privacyGroupId, eventEmitterContract, 2);
|
||||
|
||||
final LogFilterJsonParameter filter =
|
||||
blockRangeLogFilter("earliest", "latest", eventEmitterContract.getContractAddress());
|
||||
|
||||
final List<LogResult> logs =
|
||||
node.execute(privacyTransactions.privGetLogs(privacyGroupId, filter));
|
||||
|
||||
/*
|
||||
We expect one log entry per tx changing the contract value
|
||||
*/
|
||||
assertThat(logs).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLogsUsingBlockHashFilter() {
|
||||
final String privacyGroupId = createPrivacyGroup();
|
||||
final EventEmitter eventEmitterContract = deployEventEmitterContract(privacyGroupId);
|
||||
|
||||
/*
|
||||
Updating the contract value 1 times
|
||||
*/
|
||||
final PrivateTransactionReceipt updateValueReceipt =
|
||||
updateContractValue(privacyGroupId, eventEmitterContract, 1);
|
||||
final String blockHash = updateValueReceipt.getBlockHash();
|
||||
|
||||
final LogFilterJsonParameter filter =
|
||||
blockHashLogFilter(blockHash, eventEmitterContract.getContractAddress());
|
||||
|
||||
final List<LogResult> logs =
|
||||
node.execute(privacyTransactions.privGetLogs(privacyGroupId, filter));
|
||||
|
||||
assertThat(logs).hasSize(1);
|
||||
}
|
||||
|
||||
private LogFilterJsonParameter blockRangeLogFilter(
|
||||
final String fromBlock, final String toBlock, final String contractAddress) {
|
||||
return new LogFilterJsonParameter(
|
||||
fromBlock,
|
||||
toBlock,
|
||||
List.of(contractAddress),
|
||||
List.of(List.of(EVENT_EMITTER_EVENT_TOPIC)),
|
||||
null);
|
||||
}
|
||||
|
||||
private LogFilterJsonParameter blockHashLogFilter(
|
||||
final String blockHash, final String contractAddress) {
|
||||
return new LogFilterJsonParameter(
|
||||
null,
|
||||
null,
|
||||
List.of(contractAddress),
|
||||
List.of(List.of(EVENT_EMITTER_EVENT_TOPIC)),
|
||||
blockHash);
|
||||
}
|
||||
|
||||
private String createPrivacyGroup() {
|
||||
return node.execute(createPrivacyGroup("myGroupName", "my group description", node));
|
||||
}
|
||||
|
||||
private EventEmitter deployEventEmitterContract(final String privacyGroupId) {
|
||||
final EventEmitter eventEmitter =
|
||||
node.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
node.getTransactionSigningKey(),
|
||||
restriction,
|
||||
node.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(
|
||||
eventEmitter.getContractAddress(), node.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
|
||||
return eventEmitter;
|
||||
}
|
||||
|
||||
private PrivateTransactionReceipt updateContractValue(
|
||||
final String privacyGroupId, final EventEmitter eventEmitterContract, final int value) {
|
||||
final String transactionHash =
|
||||
node.execute(
|
||||
privateContractTransactions.callSmartContractWithPrivacyGroupId(
|
||||
eventEmitterContract.getContractAddress(),
|
||||
eventEmitterContract.store(BigInteger.valueOf(value)).encodeFunctionCall(),
|
||||
node.getTransactionSigningKey(),
|
||||
restriction,
|
||||
node.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
return node.execute(privacyTransactions.getPrivateTransactionReceipt(transactionHash));
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
* 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.web3j.utils.Restriction.RESTRICTED;
|
||||
import static org.web3j.utils.Restriction.UNRESTRICTED;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
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.acceptance.dsl.transaction.Transaction;
|
||||
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.Test;
|
||||
import org.testcontainers.containers.Network;
|
||||
import org.web3j.utils.Restriction;
|
||||
|
||||
public class PrivGetPrivateTransactionAcceptanceTest extends ParameterizedEnclaveTestBase {
|
||||
|
||||
private final PrivacyNode alice;
|
||||
private final PrivacyNode bob;
|
||||
|
||||
public PrivGetPrivateTransactionAcceptanceTest(
|
||||
final Restriction restriction,
|
||||
final EnclaveType enclaveType,
|
||||
final EnclaveEncryptorType enclaveEncryptorType)
|
||||
throws IOException {
|
||||
|
||||
super(restriction, enclaveType, enclaveEncryptorType);
|
||||
|
||||
final Network containerNetwork = Network.newNetwork();
|
||||
|
||||
alice =
|
||||
privacyBesu.createIbft2NodePrivacyEnabled(
|
||||
"node1",
|
||||
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
|
||||
false,
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
restriction == UNRESTRICTED,
|
||||
"0xAA");
|
||||
bob =
|
||||
privacyBesu.createIbft2NodePrivacyEnabled(
|
||||
"node2",
|
||||
PrivacyAccountResolver.BOB.resolve(enclaveEncryptorType),
|
||||
false,
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
restriction == UNRESTRICTED,
|
||||
"0xBB");
|
||||
|
||||
privacyCluster.start(alice, bob);
|
||||
|
||||
alice.verify(priv.syncingStatus(false));
|
||||
bob.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnsTransaction() {
|
||||
final Transaction<String> onlyAlice = createPrivacyGroup("Only Alice", "", alice);
|
||||
|
||||
final String privacyGroupId = alice.execute(onlyAlice);
|
||||
|
||||
final PrivateTransaction validSignedPrivateTransaction =
|
||||
getValidSignedPrivateTransaction(alice, privacyGroupId);
|
||||
final BytesValueRLPOutput rlpOutput = getRLPOutput(validSignedPrivateTransaction);
|
||||
|
||||
final Hash transactionHash =
|
||||
alice.execute(privacyTransactions.sendRawTransaction(rlpOutput.encoded().toHexString()));
|
||||
|
||||
alice.getBesu().verify(eth.expectSuccessfulTransactionReceipt(transactionHash.toString()));
|
||||
|
||||
alice
|
||||
.getBesu()
|
||||
.verify(priv.getPrivateTransaction(transactionHash, validSignedPrivateTransaction));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonExistentHashReturnsNull() {
|
||||
alice.getBesu().verify(priv.getPrivateTransactionReturnsNull(Hash.ZERO));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnsNullTransactionNotInNodesPrivacyGroup() {
|
||||
final Transaction<String> onlyAlice = createPrivacyGroup("Only Alice", "", alice);
|
||||
|
||||
final String privacyGroupId = alice.execute(onlyAlice);
|
||||
|
||||
final PrivateTransaction validSignedPrivateTransaction =
|
||||
getValidSignedPrivateTransaction(alice, privacyGroupId);
|
||||
final BytesValueRLPOutput rlpOutput = getRLPOutput(validSignedPrivateTransaction);
|
||||
|
||||
final Hash transactionHash =
|
||||
alice.execute(privacyTransactions.sendRawTransaction(rlpOutput.encoded().toHexString()));
|
||||
|
||||
alice.getBesu().verify(eth.expectSuccessfulTransactionReceipt(transactionHash.toString()));
|
||||
|
||||
bob.getBesu().verify(priv.getPrivateTransactionReturnsNull(transactionHash));
|
||||
}
|
||||
|
||||
private BytesValueRLPOutput getRLPOutput(final PrivateTransaction privateTransaction) {
|
||||
final BytesValueRLPOutput bvrlpo = new BytesValueRLPOutput();
|
||||
privateTransaction.writeTo(bvrlpo);
|
||||
return bvrlpo;
|
||||
}
|
||||
|
||||
private PrivateTransaction getValidSignedPrivateTransaction(
|
||||
final PrivacyNode node, final String privacyGoupId) {
|
||||
|
||||
org.hyperledger.besu.plugin.data.Restriction besuRestriction =
|
||||
restriction == RESTRICTED
|
||||
? org.hyperledger.besu.plugin.data.Restriction.RESTRICTED
|
||||
: org.hyperledger.besu.plugin.data.Restriction.UNRESTRICTED;
|
||||
|
||||
return PrivateTransaction.builder()
|
||||
.nonce(0)
|
||||
.gasPrice(Wei.of(999999))
|
||||
.gasLimit(3000000)
|
||||
.to(null)
|
||||
.value(Wei.ZERO)
|
||||
.payload(Bytes.wrap(new byte[] {}))
|
||||
.sender(node.getAddress())
|
||||
.privateFrom(Bytes.fromBase64String(node.getEnclaveKey()))
|
||||
.restriction(besuRestriction)
|
||||
.privacyGroupId(Bytes.fromBase64String(privacyGoupId))
|
||||
.signAndBuild(node.getBesu().getPrivacyParameters().getSigningKeyPair().get());
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* 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.UNRESTRICTED;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
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.SimpleStorage;
|
||||
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.Test;
|
||||
import org.web3j.utils.Restriction;
|
||||
|
||||
public class PrivTraceTransactionAcceptanceTest extends ParameterizedEnclaveTestBase {
|
||||
|
||||
private final PrivacyNode node;
|
||||
|
||||
private final PrivacyNode wrongNode;
|
||||
|
||||
public PrivTraceTransactionAcceptanceTest(
|
||||
final Restriction restriction,
|
||||
final EnclaveType enclaveType,
|
||||
final EnclaveEncryptorType enclaveEncryptorType)
|
||||
throws IOException {
|
||||
|
||||
super(restriction, enclaveType, enclaveEncryptorType);
|
||||
|
||||
node =
|
||||
privacyBesu.createPrivateTransactionEnabledMinerNode(
|
||||
restriction + "-node",
|
||||
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
|
||||
enclaveType,
|
||||
Optional.empty(),
|
||||
false,
|
||||
false,
|
||||
restriction == UNRESTRICTED);
|
||||
|
||||
wrongNode =
|
||||
privacyBesu.createPrivateTransactionEnabledMinerNode(
|
||||
restriction + "-node",
|
||||
PrivacyAccountResolver.BOB.resolve(enclaveEncryptorType),
|
||||
enclaveType,
|
||||
Optional.empty(),
|
||||
false,
|
||||
false,
|
||||
restriction == UNRESTRICTED);
|
||||
|
||||
privacyCluster.start(node);
|
||||
privacyCluster.start(wrongNode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTransactionTrace() throws JsonProcessingException {
|
||||
final String privacyGroupId = createPrivacyGroup();
|
||||
final SimpleStorage simpleStorageContract = deploySimpleStorageContract(privacyGroupId);
|
||||
|
||||
Hash transactionHash =
|
||||
Hash.fromHexString(doTransaction(privacyGroupId, simpleStorageContract, 0));
|
||||
|
||||
final String result =
|
||||
node.execute(privacyTransactions.privTraceTransaction(privacyGroupId, transactionHash));
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
JsonNode rootNode = mapper.readTree(result);
|
||||
JsonNode resultNode = rootNode.get("result");
|
||||
|
||||
assertThat(resultNode).isNotNull();
|
||||
assertThat(resultNode.isArray()).isTrue();
|
||||
assertThat(resultNode.size()).isGreaterThan(0);
|
||||
|
||||
JsonNode trace = resultNode.get(0);
|
||||
assertThat(trace.get("action").get("callType").asText()).isEqualTo("call");
|
||||
assertThat(trace.get("action").get("from").asText()).isEqualTo(node.getAddress().toString());
|
||||
assertThat(trace.get("action").get("input").asText()).startsWith("0x60fe47b1");
|
||||
assertThat(trace.get("action").get("to").asText())
|
||||
.isEqualTo(simpleStorageContract.getContractAddress());
|
||||
assertThat(trace.get("action").get("value").asText()).isEqualTo("0x0");
|
||||
assertThat(trace.get("blockHash").asText()).isNotEmpty();
|
||||
assertThat(trace.get("blockNumber").asInt()).isGreaterThan(0);
|
||||
assertThat(trace.get("transactionHash").asText()).isEqualTo(transactionHash.toString());
|
||||
assertThat(trace.get("type").asText()).isEqualTo("call");
|
||||
|
||||
final String wrongPrivacyGroupId = createWrongPrivacyGroup();
|
||||
|
||||
final String resultEmpty =
|
||||
wrongNode.execute(
|
||||
privacyTransactions.privTraceTransaction(wrongPrivacyGroupId, transactionHash));
|
||||
|
||||
ObjectMapper mapperEmpty = new ObjectMapper();
|
||||
|
||||
JsonNode rootNodeEmpty = mapperEmpty.readTree(resultEmpty);
|
||||
JsonNode resultNodeEmpty = rootNodeEmpty.get("result");
|
||||
|
||||
assertThat(resultNodeEmpty).isNotNull();
|
||||
assertThat(resultNodeEmpty.isArray()).isTrue();
|
||||
assertThat(resultNodeEmpty.isEmpty()).isTrue();
|
||||
|
||||
final String resultWrongHash =
|
||||
wrongNode.execute(privacyTransactions.privTraceTransaction(privacyGroupId, Hash.EMPTY));
|
||||
|
||||
ObjectMapper mapperWrongHash = new ObjectMapper();
|
||||
|
||||
JsonNode rootNodeWrongHash = mapperWrongHash.readTree(resultWrongHash);
|
||||
JsonNode resultNodeWrongHash = rootNodeWrongHash.get("result");
|
||||
|
||||
assertThat(resultNodeWrongHash).isNotNull();
|
||||
assertThat(resultNodeWrongHash.isArray()).isTrue();
|
||||
assertThat(resultNodeWrongHash.isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
private String createPrivacyGroup() {
|
||||
return node.execute(createPrivacyGroup("myGroupName", "my group description", node));
|
||||
}
|
||||
|
||||
private String createWrongPrivacyGroup() {
|
||||
return wrongNode.execute(createPrivacyGroup("myGroupName", "my group description", wrongNode));
|
||||
}
|
||||
|
||||
private SimpleStorage deploySimpleStorageContract(final String privacyGroupId) {
|
||||
final SimpleStorage simpleStorage =
|
||||
node.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
SimpleStorage.class,
|
||||
node.getTransactionSigningKey(),
|
||||
restriction,
|
||||
node.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(
|
||||
simpleStorage.getContractAddress(), node.getAddress().toString())
|
||||
.verify(simpleStorage);
|
||||
|
||||
return simpleStorage;
|
||||
}
|
||||
|
||||
private String doTransaction(
|
||||
final String privacyGroupId, final SimpleStorage simpleStorageContract, final int value) {
|
||||
return node.execute(
|
||||
privateContractTransactions.callSmartContractWithPrivacyGroupId(
|
||||
simpleStorageContract.getContractAddress(),
|
||||
simpleStorageContract.set(BigInteger.valueOf(value)).encodeFunctionCall(),
|
||||
node.getTransactionSigningKey(),
|
||||
restriction,
|
||||
node.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
}
|
||||
}
|
||||
@@ -1,407 +0,0 @@
|
||||
/*
|
||||
* 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.ethereum.core.PrivacyParameters.DEFAULT_PRIVACY;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveEncryptorType.EC;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveEncryptorType.NACL;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveType.TESSERA;
|
||||
import static org.web3j.utils.Restriction.RESTRICTED;
|
||||
|
||||
import org.hyperledger.besu.enclave.Enclave;
|
||||
import org.hyperledger.besu.enclave.EnclaveFactory;
|
||||
import org.hyperledger.besu.enclave.types.ReceiveResponse;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyAcceptanceTestBase;
|
||||
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.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.testcontainers.containers.Network;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.crypto.RawTransaction;
|
||||
import org.web3j.crypto.TransactionEncoder;
|
||||
import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
|
||||
import org.web3j.protocol.eea.crypto.PrivateTransactionEncoder;
|
||||
import org.web3j.protocol.eea.crypto.RawPrivateTransaction;
|
||||
import org.web3j.utils.Base64String;
|
||||
import org.web3j.utils.Numeric;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class PrivacyClusterAcceptanceTest extends PrivacyAcceptanceTestBase {
|
||||
|
||||
private final PrivacyNode alice;
|
||||
private final PrivacyNode bob;
|
||||
private final PrivacyNode charlie;
|
||||
private final EnclaveEncryptorType enclaveEncryptorType;
|
||||
private final Vertx vertx = Vertx.vertx();
|
||||
private final EnclaveFactory enclaveFactory = new EnclaveFactory(vertx);
|
||||
|
||||
@Parameters(name = "{0} enclave type with {1} encryptor")
|
||||
public static Collection<Object[]> enclaveParameters() {
|
||||
return Arrays.asList(
|
||||
new Object[][] {
|
||||
{TESSERA, NACL},
|
||||
{TESSERA, EC}
|
||||
});
|
||||
}
|
||||
|
||||
public PrivacyClusterAcceptanceTest(
|
||||
final EnclaveType enclaveType, final EnclaveEncryptorType enclaveEncryptorType)
|
||||
throws IOException {
|
||||
this.enclaveEncryptorType = enclaveEncryptorType;
|
||||
final Network containerNetwork = Network.newNetwork();
|
||||
alice =
|
||||
privacyBesu.createPrivateTransactionEnabledMinerNode(
|
||||
"node1",
|
||||
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
bob =
|
||||
privacyBesu.createPrivateTransactionEnabledNode(
|
||||
"node2",
|
||||
PrivacyAccountResolver.BOB.resolve(enclaveEncryptorType),
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
charlie =
|
||||
privacyBesu.createPrivateTransactionEnabledNode(
|
||||
"node3",
|
||||
PrivacyAccountResolver.CHARLIE.resolve(enclaveEncryptorType),
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
privacyCluster.start(alice, bob, charlie);
|
||||
|
||||
alice.verify(priv.syncingStatus(false));
|
||||
bob.verify(priv.syncingStatus(false));
|
||||
charlie.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
vertx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onlyAliceAndBobCanExecuteContract() {
|
||||
// Contract address is generated from sender address and transaction nonce
|
||||
final String contractAddress =
|
||||
EnclaveEncryptorType.EC.equals(enclaveEncryptorType)
|
||||
? "0x3e5d325a03ad3ce5640502219833d30b89ce3ce1"
|
||||
: "0xebf56429e6500e84442467292183d4d621359838";
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(contractAddress, alice.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
|
||||
final String transactionHash =
|
||||
alice.execute(
|
||||
privateContractTransactions.callSmartContract(
|
||||
eventEmitter.getContractAddress(),
|
||||
eventEmitter.store(BigInteger.ONE).encodeFunctionCall(),
|
||||
alice.getTransactionSigningKey(),
|
||||
RESTRICTED,
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey()));
|
||||
|
||||
final PrivateTransactionReceipt expectedReceipt =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(transactionHash));
|
||||
|
||||
bob.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
transactionHash, expectedReceipt));
|
||||
|
||||
charlie.verify(privateTransactionVerifier.noPrivateTransactionReceipt(transactionHash));
|
||||
|
||||
// When Alice executes a contract call in the wrong privacy group the transaction should pass
|
||||
// but it should NOT return any output
|
||||
final String transactionHash2 =
|
||||
alice.execute(
|
||||
privateContractTransactions.callSmartContract(
|
||||
eventEmitter.getContractAddress(),
|
||||
eventEmitter.value().encodeFunctionCall(),
|
||||
alice.getTransactionSigningKey(),
|
||||
RESTRICTED,
|
||||
alice.getEnclaveKey(),
|
||||
charlie.getEnclaveKey()));
|
||||
|
||||
final PrivateTransactionReceipt expectedReceipt2 =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(transactionHash2));
|
||||
|
||||
assertThat(expectedReceipt2.getOutput()).isEqualTo("0x");
|
||||
|
||||
charlie.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
transactionHash2, expectedReceipt2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aliceCanUsePrivDistributeTransaction() {
|
||||
// Contract address is generated from sender address and transaction nonce
|
||||
final String contractAddress =
|
||||
EnclaveEncryptorType.EC.equals(enclaveEncryptorType)
|
||||
? "0x3e5d325a03ad3ce5640502219833d30b89ce3ce1"
|
||||
: "0xebf56429e6500e84442467292183d4d621359838";
|
||||
final String receiptPrivacyGroupId =
|
||||
EnclaveEncryptorType.EC.equals(enclaveEncryptorType)
|
||||
? "MjuFB4b9Hz+f8zvkWWasxZWRjHWXU4t7B2nOHo4mekA="
|
||||
: "DyAOiF/ynpc+JXa2YAGB0bCitSlOMNm+ShmB/7M6C4w=";
|
||||
|
||||
final RawPrivateTransaction rawPrivateTransaction =
|
||||
RawPrivateTransaction.createContractTransaction(
|
||||
BigInteger.ZERO,
|
||||
BigInteger.ZERO,
|
||||
BigInteger.ZERO,
|
||||
Numeric.prependHexPrefix(EventEmitter.BINARY),
|
||||
Base64String.wrap(alice.getEnclaveKey()),
|
||||
Collections.singletonList(Base64String.wrap(bob.getEnclaveKey())),
|
||||
Base64String.wrap(receiptPrivacyGroupId),
|
||||
RESTRICTED);
|
||||
|
||||
final String signedPrivateTransaction =
|
||||
Numeric.toHexString(
|
||||
PrivateTransactionEncoder.signMessage(
|
||||
rawPrivateTransaction, Credentials.create(alice.getTransactionSigningKey())));
|
||||
final String transactionKey =
|
||||
alice.execute(privacyTransactions.privDistributeTransaction(signedPrivateTransaction));
|
||||
|
||||
final Enclave aliceEnclave = enclaveFactory.createVertxEnclave(alice.getEnclave().clientUrl());
|
||||
final ReceiveResponse aliceRR =
|
||||
aliceEnclave.receive(
|
||||
Bytes.fromHexString(transactionKey).toBase64String(), alice.getEnclaveKey());
|
||||
|
||||
final Enclave bobEnclave = enclaveFactory.createVertxEnclave(bob.getEnclave().clientUrl());
|
||||
final ReceiveResponse bobRR =
|
||||
bobEnclave.receive(
|
||||
Bytes.fromHexString(transactionKey).toBase64String(), bob.getEnclaveKey());
|
||||
|
||||
assertThat(bobRR).usingRecursiveComparison().isEqualTo(aliceRR);
|
||||
|
||||
final RawTransaction pmt =
|
||||
RawTransaction.createTransaction(
|
||||
BigInteger.ZERO,
|
||||
BigInteger.valueOf(1000),
|
||||
BigInteger.valueOf(65000),
|
||||
DEFAULT_PRIVACY.toString(),
|
||||
transactionKey);
|
||||
|
||||
final String signedPmt =
|
||||
Numeric.toHexString(
|
||||
TransactionEncoder.signMessage(
|
||||
pmt, Credentials.create(alice.getTransactionSigningKey())));
|
||||
|
||||
final String transactionHash = alice.execute(ethTransactions.sendRawTransaction(signedPmt));
|
||||
|
||||
final String receiptPrivateFrom =
|
||||
EnclaveEncryptorType.EC.equals(enclaveEncryptorType)
|
||||
? "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAES8nC4qT/KdoAoTSF3qs/47DUsDihyVbWiRjZAiyvqp9eSDkqV1RzlM+58oOwnpFRwvWNZM+AxMVxT+MvxdsqMA=="
|
||||
: "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=";
|
||||
final ArrayList<String> receiptPrivateFor =
|
||||
EnclaveEncryptorType.EC.equals(enclaveEncryptorType)
|
||||
? new ArrayList<>(
|
||||
Collections.singletonList(
|
||||
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXIgZqRA25V+3nN+Do6b5r0jiUunub6ubjPhqwHpPxP44uUYh9RKCQNRnsqCJ9PjeTnC8R3ieJk7HWAlycU1bug=="))
|
||||
: new ArrayList<>(
|
||||
Collections.singletonList("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="));
|
||||
|
||||
final PrivateTransactionReceipt expectedReceipt =
|
||||
new PrivateTransactionReceipt(
|
||||
contractAddress,
|
||||
"0xfe3b557e8fb62b89f4916b721be55ceb828dbd73",
|
||||
null,
|
||||
null, // ignored in the following call, checked separately below
|
||||
Collections.emptyList(),
|
||||
"0x023955c49d6265c579561940287449242704d5fd239ff07ea36a3fc7aface61c",
|
||||
"0x82e521ee16ff13104c5f81e8354ecaaafd5450b710b07f620204032bfe76041a",
|
||||
receiptPrivateFrom,
|
||||
receiptPrivateFor,
|
||||
receiptPrivacyGroupId,
|
||||
"0x1",
|
||||
null);
|
||||
|
||||
alice.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
transactionHash, expectedReceipt, true));
|
||||
|
||||
final PrivateTransactionReceipt alicePrivateTransactionReceipt =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(transactionHash));
|
||||
assertThat(EventEmitter.BINARY)
|
||||
.contains(alicePrivateTransactionReceipt.getOutput().substring(2));
|
||||
|
||||
bob.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
transactionHash, expectedReceipt, true));
|
||||
|
||||
final PrivateTransactionReceipt bobPrivateTransactionReceipt =
|
||||
bob.execute(privacyTransactions.getPrivateTransactionReceipt(transactionHash));
|
||||
assertThat(EventEmitter.BINARY).contains(bobPrivateTransactionReceipt.getOutput().substring(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aliceCanDeployMultipleTimesInSingleGroup() {
|
||||
final String firstDeployedAddress =
|
||||
EnclaveEncryptorType.EC.equals(enclaveEncryptorType)
|
||||
? "0x3e5d325a03ad3ce5640502219833d30b89ce3ce1"
|
||||
: "0xebf56429e6500e84442467292183d4d621359838";
|
||||
|
||||
final EventEmitter firstEventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(firstDeployedAddress, alice.getAddress().toString())
|
||||
.verify(firstEventEmitter);
|
||||
|
||||
final String secondDeployedAddress =
|
||||
EnclaveEncryptorType.EC.equals(enclaveEncryptorType)
|
||||
? "0x5194e214fae257530710d18c868df7a295d9d53b"
|
||||
: "0x10f807f8a905da5bd319196da7523c6bd768690f";
|
||||
|
||||
final EventEmitter secondEventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(secondDeployedAddress, alice.getAddress().toString())
|
||||
.verify(secondEventEmitter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canInteractWithMultiplePrivacyGroups() {
|
||||
// alice deploys contract
|
||||
final String firstDeployedAddress =
|
||||
EnclaveEncryptorType.EC.equals(enclaveEncryptorType)
|
||||
? "0x760359bc605b3848f5199829bde6b382d90fb8eb"
|
||||
: "0xff206d21150a8da5b83629d8a722f3135ed532b1";
|
||||
|
||||
final EventEmitter firstEventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey(),
|
||||
charlie.getEnclaveKey()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(firstDeployedAddress, alice.getAddress().toString())
|
||||
.verify(firstEventEmitter);
|
||||
|
||||
// charlie interacts with contract
|
||||
final String firstTransactionHash =
|
||||
charlie.execute(
|
||||
privateContractTransactions.callSmartContract(
|
||||
firstEventEmitter.getContractAddress(),
|
||||
firstEventEmitter.store(BigInteger.ONE).encodeFunctionCall(),
|
||||
charlie.getTransactionSigningKey(),
|
||||
RESTRICTED,
|
||||
charlie.getEnclaveKey(),
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey()));
|
||||
|
||||
// alice gets receipt from charlie's interaction
|
||||
final PrivateTransactionReceipt firstExpectedReceipt =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(firstTransactionHash));
|
||||
|
||||
// verify bob and charlie have access to the same receipt
|
||||
bob.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
firstTransactionHash, firstExpectedReceipt));
|
||||
charlie.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
firstTransactionHash, firstExpectedReceipt));
|
||||
|
||||
// alice deploys second contract
|
||||
final String secondDeployedAddress =
|
||||
EnclaveEncryptorType.EC.equals(enclaveEncryptorType)
|
||||
? "0x3e5d325a03ad3ce5640502219833d30b89ce3ce1"
|
||||
: "0xebf56429e6500e84442467292183d4d621359838";
|
||||
|
||||
final EventEmitter secondEventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
bob.getEnclaveKey()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(secondDeployedAddress, alice.getAddress().toString())
|
||||
.verify(secondEventEmitter);
|
||||
|
||||
// bob interacts with contract
|
||||
final String secondTransactionHash =
|
||||
bob.execute(
|
||||
privateContractTransactions.callSmartContract(
|
||||
secondEventEmitter.getContractAddress(),
|
||||
secondEventEmitter.store(BigInteger.ONE).encodeFunctionCall(),
|
||||
bob.getTransactionSigningKey(),
|
||||
RESTRICTED,
|
||||
bob.getEnclaveKey(),
|
||||
alice.getEnclaveKey()));
|
||||
|
||||
// alice gets receipt from bob's interaction
|
||||
final PrivateTransactionReceipt secondExpectedReceipt =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(secondTransactionHash));
|
||||
|
||||
bob.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
secondTransactionHash, secondExpectedReceipt));
|
||||
|
||||
// charlie cannot see the receipt
|
||||
charlie.verify(privateTransactionVerifier.noPrivateTransactionReceipt(secondTransactionHash));
|
||||
}
|
||||
}
|
||||
@@ -1,255 +0,0 @@
|
||||
/*
|
||||
* 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.enclave.testutil.EnclaveEncryptorType.EC;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveEncryptorType.NACL;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveType.TESSERA;
|
||||
import static org.web3j.utils.Restriction.RESTRICTED;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyAcceptanceTestBase;
|
||||
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.besu.util.LogConfigurator;
|
||||
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.testcontainers.containers.Network;
|
||||
import org.web3j.protocol.besu.response.privacy.PrivacyGroup;
|
||||
import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
|
||||
import org.web3j.utils.Base64String;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class PrivacyGroupAcceptanceTest extends PrivacyAcceptanceTestBase {
|
||||
|
||||
private final PrivacyNode alice;
|
||||
private final PrivacyNode bob;
|
||||
private final PrivacyNode charlie;
|
||||
|
||||
@Parameters(name = "{0} enclave type with {1} encryptor")
|
||||
public static Collection<Object[]> enclaveParameters() {
|
||||
return Arrays.asList(
|
||||
new Object[][] {
|
||||
{TESSERA, NACL},
|
||||
{TESSERA, EC}
|
||||
});
|
||||
}
|
||||
|
||||
public PrivacyGroupAcceptanceTest(
|
||||
final EnclaveType enclaveType, final EnclaveEncryptorType enclaveEncryptorType)
|
||||
throws IOException {
|
||||
|
||||
final Network containerNetwork = Network.newNetwork();
|
||||
|
||||
alice =
|
||||
privacyBesu.createPrivateTransactionEnabledMinerNode(
|
||||
"node1",
|
||||
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
bob =
|
||||
privacyBesu.createPrivateTransactionEnabledNode(
|
||||
"node2",
|
||||
PrivacyAccountResolver.BOB.resolve(enclaveEncryptorType),
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
|
||||
charlie =
|
||||
privacyBesu.createPrivateTransactionEnabledNode(
|
||||
"node3",
|
||||
PrivacyAccountResolver.CHARLIE.resolve(enclaveEncryptorType),
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
privacyCluster.start(alice, bob, charlie);
|
||||
|
||||
alice.verify(priv.syncingStatus(false));
|
||||
bob.verify(priv.syncingStatus(false));
|
||||
charlie.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nodeCanCreatePrivacyGroup() {
|
||||
LogConfigurator.setLevel("", "DEBUG");
|
||||
final String privacyGroupId =
|
||||
alice.execute(
|
||||
privacyTransactions.createPrivacyGroup(
|
||||
"myGroupName", "my group description", alice, bob));
|
||||
|
||||
assertThat(privacyGroupId).isNotNull();
|
||||
|
||||
final PrivacyGroup expected =
|
||||
new PrivacyGroup(
|
||||
privacyGroupId,
|
||||
PrivacyGroup.Type.PANTHEON,
|
||||
"myGroupName",
|
||||
"my group description",
|
||||
Base64String.wrapList(alice.getEnclaveKey(), bob.getEnclaveKey()));
|
||||
|
||||
alice.verify(privateTransactionVerifier.validPrivacyGroupCreated(expected));
|
||||
|
||||
bob.verify(privateTransactionVerifier.validPrivacyGroupCreated(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nodeCanCreatePrivacyGroupWithoutName() {
|
||||
final String privacyGroupId =
|
||||
alice.execute(
|
||||
privacyTransactions.createPrivacyGroup(null, "my group description", alice, bob));
|
||||
|
||||
assertThat(privacyGroupId).isNotNull();
|
||||
|
||||
final PrivacyGroup expected =
|
||||
new PrivacyGroup(
|
||||
privacyGroupId,
|
||||
PrivacyGroup.Type.PANTHEON,
|
||||
"",
|
||||
"my group description",
|
||||
Base64String.wrapList(alice.getEnclaveKey(), bob.getEnclaveKey()));
|
||||
|
||||
alice.verify(privateTransactionVerifier.validPrivacyGroupCreated(expected));
|
||||
|
||||
bob.verify(privateTransactionVerifier.validPrivacyGroupCreated(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nodeCanCreatePrivacyGroupWithoutDescription() {
|
||||
final String privacyGroupId =
|
||||
alice.execute(privacyTransactions.createPrivacyGroup("myGroupName", null, alice, bob));
|
||||
|
||||
assertThat(privacyGroupId).isNotNull();
|
||||
|
||||
final PrivacyGroup expected =
|
||||
new PrivacyGroup(
|
||||
privacyGroupId,
|
||||
PrivacyGroup.Type.PANTHEON,
|
||||
"myGroupName",
|
||||
"",
|
||||
Base64String.wrapList(alice.getEnclaveKey(), bob.getEnclaveKey()));
|
||||
|
||||
alice.verify(privateTransactionVerifier.validPrivacyGroupCreated(expected));
|
||||
|
||||
bob.verify(privateTransactionVerifier.validPrivacyGroupCreated(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nodeCanCreatePrivacyGroupWithoutOptionalParams() {
|
||||
final String privacyGroupId =
|
||||
alice.execute(privacyTransactions.createPrivacyGroup(null, null, alice));
|
||||
|
||||
assertThat(privacyGroupId).isNotNull();
|
||||
|
||||
final PrivacyGroup expected =
|
||||
new PrivacyGroup(
|
||||
privacyGroupId,
|
||||
PrivacyGroup.Type.PANTHEON,
|
||||
"",
|
||||
"",
|
||||
Base64String.wrapList(alice.getEnclaveKey()));
|
||||
|
||||
alice.verify(privateTransactionVerifier.validPrivacyGroupCreated(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canInteractWithMultiplePrivacyGroups() {
|
||||
final String privacyGroupIdABC =
|
||||
alice.execute(privacyTransactions.createPrivacyGroup(null, null, alice, bob, charlie));
|
||||
|
||||
final EventEmitter firstEventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
privacyGroupIdABC));
|
||||
|
||||
// charlie interacts with contract
|
||||
final String firstTransactionHash =
|
||||
charlie.execute(
|
||||
privateContractTransactions.callSmartContractWithPrivacyGroupId(
|
||||
firstEventEmitter.getContractAddress(),
|
||||
firstEventEmitter.store(BigInteger.ONE).encodeFunctionCall(),
|
||||
charlie.getTransactionSigningKey(),
|
||||
RESTRICTED,
|
||||
charlie.getEnclaveKey(),
|
||||
privacyGroupIdABC));
|
||||
|
||||
// alice gets receipt from charlie's interaction
|
||||
final PrivateTransactionReceipt firstExpectedReceipt =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(firstTransactionHash));
|
||||
|
||||
// verify bob and charlie have access to the same receipt
|
||||
bob.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
firstTransactionHash, firstExpectedReceipt));
|
||||
charlie.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
firstTransactionHash, firstExpectedReceipt));
|
||||
|
||||
// alice deploys second contract
|
||||
final String privacyGroupIdAB =
|
||||
alice.execute(privacyTransactions.createPrivacyGroup(null, null, alice, bob));
|
||||
|
||||
final EventEmitter secondEventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
alice.getEnclaveKey(),
|
||||
privacyGroupIdAB));
|
||||
|
||||
// bob interacts with contract
|
||||
final String secondTransactionHash =
|
||||
bob.execute(
|
||||
privateContractTransactions.callSmartContractWithPrivacyGroupId(
|
||||
secondEventEmitter.getContractAddress(),
|
||||
secondEventEmitter.store(BigInteger.ONE).encodeFunctionCall(),
|
||||
bob.getTransactionSigningKey(),
|
||||
RESTRICTED,
|
||||
bob.getEnclaveKey(),
|
||||
privacyGroupIdAB));
|
||||
|
||||
// alice gets receipt from bob's interaction
|
||||
final PrivateTransactionReceipt secondExpectedReceipt =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(secondTransactionHash));
|
||||
|
||||
bob.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
secondTransactionHash, secondExpectedReceipt));
|
||||
|
||||
// charlie cannot see the receipt
|
||||
charlie.verify(privateTransactionVerifier.noPrivateTransactionReceipt(secondTransactionHash));
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
/*
|
||||
* 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 java.util.Optional.empty;
|
||||
import static org.web3j.utils.Restriction.RESTRICTED;
|
||||
import static org.web3j.utils.Restriction.UNRESTRICTED;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
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.acceptance.dsl.transaction.Transaction;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.miner.MinerTransactions;
|
||||
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.Test;
|
||||
import org.web3j.utils.Restriction;
|
||||
|
||||
public class PrivacyReceiptAcceptanceTest extends ParameterizedEnclaveTestBase {
|
||||
final MinerTransactions minerTransactions = new MinerTransactions();
|
||||
|
||||
private final PrivacyNode alice;
|
||||
|
||||
public PrivacyReceiptAcceptanceTest(
|
||||
final Restriction restriction,
|
||||
final EnclaveType enclaveType,
|
||||
final EnclaveEncryptorType enclaveEncryptorType)
|
||||
throws IOException {
|
||||
super(restriction, enclaveType, enclaveEncryptorType);
|
||||
|
||||
alice =
|
||||
privacyBesu.createIbft2NodePrivacyEnabled(
|
||||
"node1",
|
||||
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
|
||||
false,
|
||||
enclaveType,
|
||||
Optional.empty(),
|
||||
false,
|
||||
false,
|
||||
restriction == UNRESTRICTED,
|
||||
"0xAA");
|
||||
privacyCluster.start(alice);
|
||||
|
||||
alice.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPrivateTransactionReceiptSuccessfulTransaction() {
|
||||
final Transaction<String> onlyAlice = createPrivacyGroup("Only Alice", "", alice);
|
||||
|
||||
final String privacyGroupId = alice.execute(onlyAlice);
|
||||
|
||||
final PrivateTransaction validTransaction =
|
||||
createSignedTransaction(alice, privacyGroupId, empty());
|
||||
final BytesValueRLPOutput rlpOutput = getRLPOutput(validTransaction);
|
||||
|
||||
final Hash transactionHash =
|
||||
alice.execute(privacyTransactions.sendRawTransaction(rlpOutput.encoded().toHexString()));
|
||||
|
||||
// Successful PMT
|
||||
alice.getBesu().verify(eth.expectSuccessfulTransactionReceipt(transactionHash.toString()));
|
||||
// Successful private transaction
|
||||
alice.getBesu().verify(priv.getSuccessfulTransactionReceipt(transactionHash));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPrivateTransactionReceiptFailedTransaction() {
|
||||
final Transaction<String> onlyAlice = createPrivacyGroup("Only Alice", "", alice);
|
||||
|
||||
final String privacyGroupId = alice.execute(onlyAlice);
|
||||
|
||||
final PrivateTransaction invalidPayloadTransaction =
|
||||
createSignedTransaction(
|
||||
alice, privacyGroupId, Optional.of(Bytes.fromBase64String("invalidPayload")));
|
||||
final BytesValueRLPOutput rlpOutput = getRLPOutput(invalidPayloadTransaction);
|
||||
|
||||
final Hash transactionHash =
|
||||
alice.execute(privacyTransactions.sendRawTransaction(rlpOutput.encoded().toHexString()));
|
||||
|
||||
// Successful PMT
|
||||
alice.getBesu().verify(eth.expectSuccessfulTransactionReceipt(transactionHash.toString()));
|
||||
// Failed private transaction
|
||||
alice.getBesu().verify(priv.getFailedTransactionReceipt(transactionHash));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPrivateTransactionReceiptInvalidTransaction() {
|
||||
final Transaction<String> onlyAlice = createPrivacyGroup("Only Alice", "", alice);
|
||||
|
||||
final String privacyGroupId = alice.execute(onlyAlice);
|
||||
|
||||
final PrivateTransaction validTransaction =
|
||||
createSignedTransaction(alice, privacyGroupId, empty());
|
||||
final BytesValueRLPOutput rlpOutput = getRLPOutput(validTransaction);
|
||||
|
||||
// Stop mining, to allow adding duplicate nonce block
|
||||
alice.getBesu().execute(minerTransactions.minerStop());
|
||||
|
||||
final Hash transactionHash1 =
|
||||
alice.execute(privacyTransactions.sendRawTransaction(rlpOutput.encoded().toHexString()));
|
||||
final Hash transactionHash2 =
|
||||
alice.execute(privacyTransactions.sendRawTransaction(rlpOutput.encoded().toHexString()));
|
||||
|
||||
// Start mining again
|
||||
alice.getBesu().execute(minerTransactions.minerStart());
|
||||
|
||||
// Successful PMTs
|
||||
alice.getBesu().verify(eth.expectSuccessfulTransactionReceipt(transactionHash1.toString()));
|
||||
alice.getBesu().verify(eth.expectSuccessfulTransactionReceipt(transactionHash2.toString()));
|
||||
// Successful first private transaction
|
||||
alice.getBesu().verify(priv.getSuccessfulTransactionReceipt(transactionHash1));
|
||||
// Invalid second private transaction
|
||||
alice.getBesu().verify(priv.getInvalidTransactionReceipt(transactionHash2));
|
||||
}
|
||||
|
||||
private BytesValueRLPOutput getRLPOutput(final PrivateTransaction privateTransaction) {
|
||||
final BytesValueRLPOutput bvrlpo = new BytesValueRLPOutput();
|
||||
privateTransaction.writeTo(bvrlpo);
|
||||
return bvrlpo;
|
||||
}
|
||||
|
||||
private PrivateTransaction createSignedTransaction(
|
||||
final PrivacyNode node, final String privacyGoupId, final Optional<Bytes> payload) {
|
||||
|
||||
org.hyperledger.besu.plugin.data.Restriction besuRestriction =
|
||||
restriction == RESTRICTED
|
||||
? org.hyperledger.besu.plugin.data.Restriction.RESTRICTED
|
||||
: org.hyperledger.besu.plugin.data.Restriction.UNRESTRICTED;
|
||||
|
||||
final Bytes defaultPayload = Bytes.wrap(new byte[] {});
|
||||
return PrivateTransaction.builder()
|
||||
.nonce(0)
|
||||
.gasPrice(Wei.of(999999))
|
||||
.gasLimit(3000000)
|
||||
.to(null)
|
||||
.value(Wei.ZERO)
|
||||
.payload(payload.orElse(defaultPayload))
|
||||
.sender(node.getAddress())
|
||||
.privateFrom(Bytes.fromBase64String(node.getEnclaveKey()))
|
||||
.restriction(besuRestriction)
|
||||
.privacyGroupId(Bytes.fromBase64String(privacyGoupId))
|
||||
.signAndBuild(node.getBesu().getPrivacyParameters().getSigningKeyPair().get());
|
||||
}
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
/*
|
||||
* 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.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
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.CrossContractReader;
|
||||
import org.hyperledger.besu.tests.web3j.generated.EventEmitter;
|
||||
import org.hyperledger.besu.tests.web3j.generated.RemoteSimpleStorage;
|
||||
import org.hyperledger.besu.tests.web3j.generated.SimpleStorage;
|
||||
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.testcontainers.containers.Network;
|
||||
import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
|
||||
import org.web3j.protocol.core.RemoteFunctionCall;
|
||||
import org.web3j.protocol.core.methods.response.TransactionReceipt;
|
||||
import org.web3j.protocol.exceptions.TransactionException;
|
||||
import org.web3j.tx.exceptions.ContractCallException;
|
||||
import org.web3j.utils.Restriction;
|
||||
|
||||
public class PrivateContractPublicStateAcceptanceTest extends ParameterizedEnclaveTestBase {
|
||||
|
||||
private final PrivacyNode transactionNode;
|
||||
|
||||
public PrivateContractPublicStateAcceptanceTest(
|
||||
final Restriction restriction,
|
||||
final EnclaveType enclaveType,
|
||||
final EnclaveEncryptorType enclaveEncryptorType)
|
||||
throws IOException {
|
||||
super(restriction, enclaveType, enclaveEncryptorType);
|
||||
final Network containerNetwork = Network.newNetwork();
|
||||
|
||||
final PrivacyNode minerNode =
|
||||
privacyBesu.createPrivateTransactionEnabledMinerNode(
|
||||
restriction + "-miner-node",
|
||||
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
restriction == UNRESTRICTED);
|
||||
|
||||
transactionNode =
|
||||
privacyBesu.createPrivateTransactionEnabledNode(
|
||||
restriction + "-transaction-node",
|
||||
PrivacyAccountResolver.BOB.resolve(enclaveEncryptorType),
|
||||
enclaveType,
|
||||
Optional.of(containerNetwork),
|
||||
false,
|
||||
false,
|
||||
restriction == UNRESTRICTED);
|
||||
|
||||
privacyCluster.start(minerNode, transactionNode);
|
||||
|
||||
minerNode.verify(priv.syncingStatus(false));
|
||||
transactionNode.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mustAllowAccessToPublicStateFromPrivateTx() throws Exception {
|
||||
final EventEmitter publicEventEmitter =
|
||||
transactionNode.execute(contractTransactions.createSmartContract(EventEmitter.class));
|
||||
|
||||
final TransactionReceipt receipt = publicEventEmitter.store(BigInteger.valueOf(12)).send();
|
||||
assertThat(receipt).isNotNull();
|
||||
|
||||
final CrossContractReader reader =
|
||||
transactionNode.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
CrossContractReader.class,
|
||||
transactionNode.getTransactionSigningKey(),
|
||||
transactionNode.getEnclaveKey()));
|
||||
|
||||
final RemoteFunctionCall<BigInteger> remoteFunctionCall =
|
||||
reader.read(publicEventEmitter.getContractAddress());
|
||||
final BigInteger result = remoteFunctionCall.send();
|
||||
|
||||
assertThat(result).isEqualTo(BigInteger.valueOf(12));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mustNotAllowAccessToPrivateStateFromPublicTx() throws Exception {
|
||||
final EventEmitter privateEventEmitter =
|
||||
transactionNode.execute(
|
||||
(privateContractTransactions.createSmartContract(
|
||||
EventEmitter.class,
|
||||
transactionNode.getTransactionSigningKey(),
|
||||
transactionNode.getEnclaveKey())));
|
||||
|
||||
final TransactionReceipt receipt = privateEventEmitter.store(BigInteger.valueOf(12)).send();
|
||||
assertThat(receipt).isNotNull();
|
||||
|
||||
final CrossContractReader publicReader =
|
||||
transactionNode.execute(
|
||||
contractTransactions.createSmartContract(CrossContractReader.class));
|
||||
final RemoteFunctionCall<BigInteger> functionCall =
|
||||
publicReader.read(privateEventEmitter.getContractAddress());
|
||||
assertThatThrownBy(functionCall::send).isInstanceOf(ContractCallException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void privateContractMustNotBeAbleToCallPublicContractWhichChangesState() throws Exception {
|
||||
final CrossContractReader privateReader =
|
||||
transactionNode.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
CrossContractReader.class,
|
||||
transactionNode.getTransactionSigningKey(),
|
||||
transactionNode.getEnclaveKey()));
|
||||
|
||||
final CrossContractReader publicReader =
|
||||
transactionNode.execute(
|
||||
contractTransactions.createSmartContract(CrossContractReader.class));
|
||||
|
||||
assertThatExceptionOfType(TransactionException.class)
|
||||
.isThrownBy(() -> privateReader.incrementRemote(publicReader.getContractAddress()).send())
|
||||
.returns(
|
||||
"0x", e -> ((PrivateTransactionReceipt) e.getTransactionReceipt().get()).getOutput());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void privateContractMustNotBeAbleToCallPublicContractWhichInstantiatesContract()
|
||||
throws Exception {
|
||||
final CrossContractReader privateReader =
|
||||
transactionNode.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
CrossContractReader.class,
|
||||
transactionNode.getTransactionSigningKey(),
|
||||
transactionNode.getEnclaveKey()));
|
||||
|
||||
final CrossContractReader publicReader =
|
||||
transactionNode.execute(
|
||||
contractTransactions.createSmartContract(CrossContractReader.class));
|
||||
|
||||
assertThatExceptionOfType(TransactionException.class)
|
||||
.isThrownBy(() -> privateReader.deployRemote(publicReader.getContractAddress()).send())
|
||||
.returns(0, e -> e.getTransactionReceipt().get().getLogs().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void privateContractMustNotBeAbleToCallSelfDestructOnPublicContract() throws Exception {
|
||||
final CrossContractReader privateReader =
|
||||
transactionNode.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
CrossContractReader.class,
|
||||
transactionNode.getTransactionSigningKey(),
|
||||
transactionNode.getEnclaveKey()));
|
||||
|
||||
final CrossContractReader publicReader =
|
||||
transactionNode
|
||||
.getBesu()
|
||||
.execute(contractTransactions.createSmartContract(CrossContractReader.class));
|
||||
|
||||
assertThatExceptionOfType(TransactionException.class)
|
||||
.isThrownBy(() -> privateReader.remoteDestroy(publicReader.getContractAddress()).send())
|
||||
.withMessage(
|
||||
"Transaction null has failed with status: 0x0. Gas used: unknown. Revert reason: '0x'.")
|
||||
.returns(
|
||||
"0x", e -> ((PrivateTransactionReceipt) e.getTransactionReceipt().get()).getOutput());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void privateContractCanCallPublicContractThatCallsPublicContract() throws Exception {
|
||||
final SimpleStorage simpleStorage =
|
||||
transactionNode
|
||||
.getBesu()
|
||||
.execute(contractTransactions.createSmartContract(SimpleStorage.class));
|
||||
|
||||
final RemoteSimpleStorage remoteSimpleStorage =
|
||||
transactionNode
|
||||
.getBesu()
|
||||
.execute(contractTransactions.createSmartContract(RemoteSimpleStorage.class));
|
||||
|
||||
remoteSimpleStorage.setRemote(simpleStorage.getContractAddress()).send();
|
||||
|
||||
final RemoteSimpleStorage reallyRemoteSimpleStorage =
|
||||
transactionNode
|
||||
.getBesu()
|
||||
.execute(contractTransactions.createSmartContract(RemoteSimpleStorage.class));
|
||||
|
||||
reallyRemoteSimpleStorage.setRemote(remoteSimpleStorage.getContractAddress()).send();
|
||||
|
||||
simpleStorage.set(BigInteger.valueOf(42)).send();
|
||||
|
||||
assertThat(simpleStorage.get().send()).isEqualTo(BigInteger.valueOf(42));
|
||||
assertThat(remoteSimpleStorage.get().send()).isEqualTo(BigInteger.valueOf(42));
|
||||
assertThat(reallyRemoteSimpleStorage.get().send()).isEqualTo(BigInteger.valueOf(42));
|
||||
|
||||
final RemoteSimpleStorage privateRemoteSimpleStorage =
|
||||
transactionNode.execute(
|
||||
privateContractTransactions.createSmartContract(
|
||||
RemoteSimpleStorage.class,
|
||||
transactionNode.getTransactionSigningKey(),
|
||||
transactionNode.getEnclaveKey()));
|
||||
|
||||
privateRemoteSimpleStorage.setRemote(simpleStorage.getContractAddress()).send();
|
||||
assertThat(privateRemoteSimpleStorage.get().send()).isEqualTo(BigInteger.valueOf(42));
|
||||
|
||||
privateRemoteSimpleStorage.setRemote(reallyRemoteSimpleStorage.getContractAddress()).send();
|
||||
assertThat(privateRemoteSimpleStorage.get().send()).isEqualTo(BigInteger.valueOf(42));
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* 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.EnclaveEncryptorType;
|
||||
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,
|
||||
final EnclaveEncryptorType enclaveEncryptorType)
|
||||
throws IOException {
|
||||
|
||||
super(restriction, enclaveType, enclaveEncryptorType);
|
||||
|
||||
alice =
|
||||
privacyBesu.createIbft2NodePrivacyEnabledWithGenesis(
|
||||
"node1",
|
||||
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
|
||||
true,
|
||||
enclaveType,
|
||||
Optional.empty(),
|
||||
false,
|
||||
false,
|
||||
restriction == UNRESTRICTED,
|
||||
"AA");
|
||||
|
||||
privacyCluster.start(alice);
|
||||
alice.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
* 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.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.acceptance.dsl.privacy.util.LogFilterJsonParameter;
|
||||
import org.hyperledger.besu.tests.web3j.generated.EventEmitter;
|
||||
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
|
||||
import org.web3j.protocol.core.methods.response.EthLog.LogResult;
|
||||
import org.web3j.utils.Restriction;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class PrivateLogFilterAcceptanceTest extends ParameterizedEnclaveTestBase {
|
||||
|
||||
private final PrivacyNode node;
|
||||
|
||||
public PrivateLogFilterAcceptanceTest(
|
||||
final Restriction restriction,
|
||||
final EnclaveType enclaveType,
|
||||
final EnclaveEncryptorType enclaveEncryptorType)
|
||||
throws IOException {
|
||||
|
||||
super(restriction, enclaveType, enclaveEncryptorType);
|
||||
|
||||
node =
|
||||
privacyBesu.createPrivateTransactionEnabledMinerNode(
|
||||
restriction + "-node",
|
||||
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
|
||||
enclaveType,
|
||||
Optional.empty(),
|
||||
false,
|
||||
false,
|
||||
restriction == UNRESTRICTED);
|
||||
|
||||
privacyCluster.start(node);
|
||||
node.verify(priv.syncingStatus(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void installAndUninstallFilter() {
|
||||
final String privacyGroupId = createPrivacyGroup();
|
||||
final EventEmitter eventEmitterContract = deployEventEmitterContract(privacyGroupId);
|
||||
|
||||
final LogFilterJsonParameter filter =
|
||||
blockRangeLogFilter("earliest", "latest", eventEmitterContract.getContractAddress());
|
||||
|
||||
final String filterId = node.execute(privacyTransactions.newFilter(privacyGroupId, filter));
|
||||
|
||||
final boolean filterUninstalled =
|
||||
node.execute(privacyTransactions.uninstallFilter(privacyGroupId, filterId));
|
||||
|
||||
assertThat(filterUninstalled).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFilterLogs() {
|
||||
final String privacyGroupId = createPrivacyGroup();
|
||||
final EventEmitter eventEmitterContract = deployEventEmitterContract(privacyGroupId);
|
||||
|
||||
final LogFilterJsonParameter filter =
|
||||
blockRangeLogFilter("earliest", "latest", eventEmitterContract.getContractAddress());
|
||||
final String filterId = node.execute(privacyTransactions.newFilter(privacyGroupId, filter));
|
||||
|
||||
updateContractValue(privacyGroupId, eventEmitterContract, 1);
|
||||
|
||||
final List<LogResult> logs =
|
||||
node.execute(privacyTransactions.getFilterLogs(privacyGroupId, filterId));
|
||||
|
||||
assertThat(logs).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFilterChanges() {
|
||||
final String privacyGroupId = createPrivacyGroup();
|
||||
final EventEmitter eventEmitterContract = deployEventEmitterContract(privacyGroupId);
|
||||
|
||||
final LogFilterJsonParameter filter =
|
||||
blockRangeLogFilter("earliest", "latest", eventEmitterContract.getContractAddress());
|
||||
final String filterId = node.execute(privacyTransactions.newFilter(privacyGroupId, filter));
|
||||
|
||||
updateContractValue(privacyGroupId, eventEmitterContract, 1);
|
||||
updateContractValue(privacyGroupId, eventEmitterContract, 2);
|
||||
|
||||
assertThat(node.execute(privacyTransactions.getFilterChanges(privacyGroupId, filterId)))
|
||||
.hasSize(2);
|
||||
|
||||
updateContractValue(privacyGroupId, eventEmitterContract, 3);
|
||||
|
||||
assertThat(node.execute(privacyTransactions.getFilterChanges(privacyGroupId, filterId)))
|
||||
.hasSize(1);
|
||||
}
|
||||
|
||||
private LogFilterJsonParameter blockRangeLogFilter(
|
||||
final String fromBlock, final String toBlock, final String contractAddress) {
|
||||
return new LogFilterJsonParameter(
|
||||
fromBlock, toBlock, List.of(contractAddress), Collections.emptyList(), null);
|
||||
}
|
||||
|
||||
private String createPrivacyGroup() {
|
||||
return node.execute(createPrivacyGroup("myGroupName", "my group description", node));
|
||||
}
|
||||
|
||||
private EventEmitter deployEventEmitterContract(final String privacyGroupId) {
|
||||
final EventEmitter eventEmitter =
|
||||
node.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
node.getTransactionSigningKey(),
|
||||
restriction,
|
||||
node.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(
|
||||
eventEmitter.getContractAddress(), node.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
|
||||
return eventEmitter;
|
||||
}
|
||||
|
||||
private PrivateTransactionReceipt updateContractValue(
|
||||
final String privacyGroupId, final EventEmitter eventEmitterContract, final int value) {
|
||||
final String transactionHash =
|
||||
node.execute(
|
||||
privateContractTransactions.callSmartContractWithPrivacyGroupId(
|
||||
eventEmitterContract.getContractAddress(),
|
||||
eventEmitterContract.store(BigInteger.valueOf(value)).encodeFunctionCall(),
|
||||
node.getTransactionSigningKey(),
|
||||
restriction,
|
||||
node.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
return node.execute(privacyTransactions.getPrivateTransactionReceipt(transactionHash));
|
||||
}
|
||||
}
|
||||
@@ -1,491 +0,0 @@
|
||||
/*
|
||||
* 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.multitenancy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveEncryptorType.EC;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveEncryptorType.NACL;
|
||||
import static org.hyperledger.enclave.testutil.EnclaveType.TESSERA;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;
|
||||
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.privacy.contract.CallPrivateSmartContractFunction;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.transaction.CreateFlexiblePrivacyGroupTransaction;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.util.LogFilterJsonParameter;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.perm.PermissioningTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyRequestFactory;
|
||||
import org.hyperledger.besu.tests.acceptance.privacy.FlexiblePrivacyAcceptanceTestBase;
|
||||
import org.hyperledger.besu.tests.web3j.generated.EventEmitter;
|
||||
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
|
||||
import org.hyperledger.enclave.testutil.EnclaveType;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
|
||||
import org.web3j.protocol.core.methods.response.EthCall;
|
||||
import org.web3j.utils.Base64String;
|
||||
import org.web3j.utils.Restriction;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class FlexibleMultiTenancyAcceptanceTest extends FlexiblePrivacyAcceptanceTestBase {
|
||||
|
||||
private final EnclaveType enclaveType;
|
||||
private final EnclaveEncryptorType enclaveEncryptorType;
|
||||
|
||||
public FlexibleMultiTenancyAcceptanceTest(
|
||||
final EnclaveType enclaveType, final EnclaveEncryptorType enclaveEncryptorType) {
|
||||
this.enclaveType = enclaveType;
|
||||
this.enclaveEncryptorType = enclaveEncryptorType;
|
||||
}
|
||||
|
||||
@Parameterized.Parameters(name = "{0} enclave type with {1} encryptor")
|
||||
public static Collection<Object[]> enclaveParameters() {
|
||||
return Arrays.asList(
|
||||
new Object[][] {
|
||||
{TESSERA, NACL},
|
||||
{TESSERA, EC}
|
||||
});
|
||||
}
|
||||
|
||||
private static final PermissioningTransactions permissioningTransactions =
|
||||
new PermissioningTransactions();
|
||||
private static final long VALUE_SET = 10L;
|
||||
|
||||
private PrivacyNode alice;
|
||||
private MultiTenancyPrivacyNode aliceMultiTenancyPrivacyNode;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
alice =
|
||||
privacyBesu.createFlexiblePrivacyGroupEnabledMinerNode(
|
||||
"node1",
|
||||
PrivacyAccountResolver.MULTI_TENANCY.resolve(enclaveEncryptorType),
|
||||
true,
|
||||
enclaveType,
|
||||
Optional.empty());
|
||||
final BesuNode aliceBesu = alice.getBesu();
|
||||
privacyCluster.startNodes(alice);
|
||||
final String alice1Token =
|
||||
aliceBesu.execute(permissioningTransactions.createSuccessfulLogin("user", "pegasys"));
|
||||
aliceBesu.useAuthenticationTokenInHeaderForJsonRpc(alice1Token);
|
||||
final String alice2Token =
|
||||
aliceBesu.execute(permissioningTransactions.createSuccessfulLogin("user2", "Password2"));
|
||||
final String alice3Token =
|
||||
aliceBesu.execute(permissioningTransactions.createSuccessfulLogin("user3", "Password3"));
|
||||
privacyCluster.awaitPeerCount(alice);
|
||||
|
||||
final String alice1EnclaveKey = alice.getEnclave().getPublicKeys().get(0);
|
||||
final String alice2EnclaveKey = alice.getEnclave().getPublicKeys().get(1);
|
||||
final String alice3EnclaveKey = alice.getEnclave().getPublicKeys().get(2);
|
||||
|
||||
aliceMultiTenancyPrivacyNode = new MultiTenancyPrivacyNode(alice);
|
||||
aliceMultiTenancyPrivacyNode
|
||||
.addTenantWithToken(alice1EnclaveKey, alice1Token)
|
||||
.addTenantWithToken(alice2EnclaveKey, alice2Token)
|
||||
.addTenantWithToken(alice3EnclaveKey, alice3Token);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
privacyCluster.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPrivacyGroup() {
|
||||
createFlexiblePrivacyGroup(alice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPrivacyGroupWithAllTenants() {
|
||||
final MultiTenancyPrivacyGroup privacyGroup = new MultiTenancyPrivacyGroup();
|
||||
privacyGroup.addNodeWithTenants(
|
||||
aliceMultiTenancyPrivacyNode, aliceMultiTenancyPrivacyNode.getTenants());
|
||||
createFlexiblePrivacyGroup(privacyGroup);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noAccessWhenNotAMember() {
|
||||
final MultiTenancyPrivacyGroup twoTenantsFromAlice = new MultiTenancyPrivacyGroup();
|
||||
final List<String> tenants = aliceMultiTenancyPrivacyNode.getTenants();
|
||||
final String removedTenant = tenants.remove(tenants.size() - 1);
|
||||
twoTenantsFromAlice.addNodeWithTenants(aliceMultiTenancyPrivacyNode, tenants);
|
||||
final String privacyGroupId = createFlexiblePrivacyGroup(twoTenantsFromAlice);
|
||||
|
||||
final MultiTenancyPrivacyNode multiTenancyPrivacyNode =
|
||||
twoTenantsFromAlice.getPrivacyNodes().get(0);
|
||||
final String tenant = tenants.get(0);
|
||||
final PrivacyNode privacyNode = multiTenancyPrivacyNode.getPrivacyNode();
|
||||
final BesuNode privacyNodeBesu = privacyNode.getBesu();
|
||||
privacyNodeBesu.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(tenant));
|
||||
final EventEmitter eventEmitter =
|
||||
privacyNode.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
privacyNode.getTransactionSigningKey(),
|
||||
tenant,
|
||||
privacyGroupId));
|
||||
|
||||
final String transactionHash = getContractDeploymentCommitmentHash(eventEmitter);
|
||||
|
||||
// check that a member can get the transaction receipt
|
||||
privacyNodeBesu.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(tenant));
|
||||
privacyNode.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
transactionHash,
|
||||
(PrivateTransactionReceipt) eventEmitter.getTransactionReceipt().get()));
|
||||
final String actual =
|
||||
privacyNode
|
||||
.execute(
|
||||
privacyTransactions.privGetCode(
|
||||
privacyGroupId,
|
||||
Address.fromHexString(eventEmitter.getContractAddress()),
|
||||
"latest"))
|
||||
.toHexString();
|
||||
assertThat(EventEmitter.BINARY).contains(actual.substring(2));
|
||||
|
||||
// check that getting the transaction receipt does not work if you are not a member
|
||||
privacyNodeBesu.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(removedTenant));
|
||||
privacyNode.verify(
|
||||
privateTransactionVerifier.noPrivateTransactionReceipt(
|
||||
transactionHash)); // returning null because the RPC is using the enclave key
|
||||
|
||||
// check that getting the code of the event emitter does not work when you are not a member
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
privacyNode.execute(
|
||||
privacyTransactions.privGetCode(
|
||||
privacyGroupId,
|
||||
Address.fromHexString(eventEmitter.getContractAddress()),
|
||||
"latest")))
|
||||
.hasMessageContaining("Unauthorized");
|
||||
|
||||
final LogFilterJsonParameter filterParameter =
|
||||
new LogFilterJsonParameter(
|
||||
"earliest",
|
||||
"latest",
|
||||
List.of(eventEmitter.getContractAddress()),
|
||||
Collections.emptyList(),
|
||||
null);
|
||||
|
||||
// create a valid filter
|
||||
privacyNodeBesu.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(tenant));
|
||||
final String filterId =
|
||||
privacyNode.execute(privacyTransactions.newFilter(privacyGroupId, filterParameter));
|
||||
|
||||
privacyNodeBesu.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(tenant));
|
||||
final CallPrivateSmartContractFunction storeTransaction =
|
||||
privateContractTransactions.callSmartContractWithPrivacyGroupId(
|
||||
eventEmitter.getContractAddress(),
|
||||
eventEmitter.store(BigInteger.valueOf(VALUE_SET)).encodeFunctionCall(),
|
||||
privacyNode.getTransactionSigningKey(),
|
||||
Restriction.RESTRICTED,
|
||||
tenant,
|
||||
privacyGroupId);
|
||||
final String storeTransactionHash = privacyNode.execute(storeTransaction);
|
||||
|
||||
privacyNode.execute(privacyTransactions.getPrivateTransactionReceipt(storeTransactionHash));
|
||||
|
||||
// check that getting the filter changes works for a member
|
||||
assertThat(privacyNode.execute(privacyTransactions.getFilterChanges(privacyGroupId, filterId)))
|
||||
.hasSize(1);
|
||||
|
||||
// check that getting the filter changes does not work if you are not a member
|
||||
privacyNodeBesu.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(removedTenant));
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
privacyNode.execute(privacyTransactions.getFilterChanges(privacyGroupId, filterId)))
|
||||
.hasMessageContaining("Unauthorized");
|
||||
|
||||
// check that getting the filter logs works for a member
|
||||
privacyNodeBesu.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(tenant));
|
||||
assertThat(privacyNode.execute(privacyTransactions.getFilterLogs(privacyGroupId, filterId)))
|
||||
.hasSize(3); // create privacy group, deploy event emitter, store on event emitter
|
||||
|
||||
// check that getting the filter logs does not work if you are not a member
|
||||
privacyNodeBesu.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(removedTenant));
|
||||
assertThatThrownBy(
|
||||
() -> privacyNode.execute(privacyTransactions.getFilterLogs(privacyGroupId, filterId)))
|
||||
.hasMessageContaining("Unauthorized");
|
||||
|
||||
// check that getting the logs works for a member
|
||||
privacyNodeBesu.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(tenant));
|
||||
assertThat(
|
||||
privacyNode.execute(privacyTransactions.privGetLogs(privacyGroupId, filterParameter)))
|
||||
.hasSize(3); // create privacy group, deploy event emitter, store on event emitter
|
||||
|
||||
// check that getting the logs does not work if you are not a member
|
||||
privacyNodeBesu.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(removedTenant));
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
privacyNode.execute(
|
||||
privacyTransactions.privGetLogs(privacyGroupId, filterParameter)))
|
||||
.hasMessageContaining("Unauthorized");
|
||||
|
||||
final List<Base64String> base64StringList =
|
||||
tenants.stream().map(Base64String::wrap).collect(Collectors.toList());
|
||||
|
||||
// check that a member can find the on-chain privacy group
|
||||
privacyNode
|
||||
.getBesu()
|
||||
.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(tenant));
|
||||
final List<PrivacyRequestFactory.FlexiblePrivacyGroup> group =
|
||||
privacyNode.execute(
|
||||
privacyTransactions.findFlexiblePrivacyGroup(
|
||||
Base64String.unwrapList(base64StringList)));
|
||||
assertThat(group.size()).isEqualTo(1);
|
||||
assertThat(group.get(0).getMembers()).containsAll(base64StringList).hasSize(2);
|
||||
|
||||
// check that when you are not a member you cannot find the privacy group
|
||||
privacyNode
|
||||
.getBesu()
|
||||
.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(removedTenant));
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
privacyNode.execute(
|
||||
privacyTransactions.findFlexiblePrivacyGroup(
|
||||
Base64String.unwrapList(base64StringList))))
|
||||
.hasMessageContaining("Error finding flexible privacy group");
|
||||
|
||||
// check that a member can do a priv_call
|
||||
privacyNode
|
||||
.getBesu()
|
||||
.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(tenant));
|
||||
final EthCall readValue =
|
||||
privacyNode.execute(
|
||||
privacyTransactions.privCall(
|
||||
privacyGroupId, eventEmitter, eventEmitter.value().encodeFunctionCall()));
|
||||
assertThat(new BigInteger(readValue.getValue().substring(2), 16))
|
||||
.isEqualByComparingTo(BigInteger.valueOf(VALUE_SET));
|
||||
|
||||
// check that when you are not a member you cannot do a priv_call
|
||||
privacyNode
|
||||
.getBesu()
|
||||
.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(removedTenant));
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
privacyNode.execute(
|
||||
privacyTransactions.privCall(
|
||||
privacyGroupId, eventEmitter, eventEmitter.value().encodeFunctionCall())))
|
||||
.hasMessageContaining("Unauthorized");
|
||||
|
||||
// check that a member can do a priv_getTransaction
|
||||
privacyNode
|
||||
.getBesu()
|
||||
.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(tenant));
|
||||
final PrivacyRequestFactory.GetPrivateTransactionResponse privTransaction =
|
||||
privacyNode.execute(privacyTransactions.privGetTransaction(storeTransactionHash));
|
||||
assertThat(privTransaction.getResult().getPrivacyGroupId()).isEqualTo(privacyGroupId);
|
||||
|
||||
// check that when you are not a member you cannot do a priv_getTransaction
|
||||
privacyNode
|
||||
.getBesu()
|
||||
.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(removedTenant));
|
||||
assertThatThrownBy(
|
||||
() -> privacyNode.execute(privacyTransactions.privGetTransaction(storeTransactionHash)))
|
||||
.hasMessageContaining(
|
||||
"Expecting actual not to be null"); // TODO: returning null because the RPC is using the
|
||||
// enclave key
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removedMemberCannotGetFilterChanges() {
|
||||
final MultiTenancyPrivacyGroup allTenantsFromAlice = new MultiTenancyPrivacyGroup();
|
||||
final List<String> tenants = aliceMultiTenancyPrivacyNode.getTenants();
|
||||
allTenantsFromAlice.addNodeWithTenants(aliceMultiTenancyPrivacyNode, tenants);
|
||||
final String privacyGroupId = createFlexiblePrivacyGroup(allTenantsFromAlice);
|
||||
final MultiTenancyPrivacyNode multiTenancyPrivacyNode =
|
||||
allTenantsFromAlice.getPrivacyNodes().get(0);
|
||||
final String groupCreatingTenant = allTenantsFromAlice.getGroupCreatingTenant();
|
||||
final String tenantToBeRemoved =
|
||||
tenants.stream().filter(t -> !t.equals(groupCreatingTenant)).findFirst().orElseThrow();
|
||||
final PrivacyNode groupCreatingPrivacyNode = allTenantsFromAlice.getGroupCreatingPrivacyNode();
|
||||
final BesuNode groupCreatingPrivacyNodeBesu = groupCreatingPrivacyNode.getBesu();
|
||||
groupCreatingPrivacyNodeBesu.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(groupCreatingTenant));
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
groupCreatingPrivacyNode.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
groupCreatingPrivacyNode.getTransactionSigningKey(),
|
||||
groupCreatingTenant,
|
||||
privacyGroupId));
|
||||
|
||||
final LogFilterJsonParameter filterParameter =
|
||||
new LogFilterJsonParameter(
|
||||
"earliest",
|
||||
"latest",
|
||||
List.of(eventEmitter.getContractAddress()),
|
||||
Collections.emptyList(),
|
||||
null);
|
||||
|
||||
final String filterId =
|
||||
groupCreatingPrivacyNode.execute(
|
||||
privacyTransactions.newFilter(privacyGroupId, filterParameter));
|
||||
|
||||
final CallPrivateSmartContractFunction storeTransaction =
|
||||
privateContractTransactions.callSmartContractWithPrivacyGroupId(
|
||||
eventEmitter.getContractAddress(),
|
||||
eventEmitter.store(BigInteger.valueOf(VALUE_SET)).encodeFunctionCall(),
|
||||
groupCreatingPrivacyNode.getTransactionSigningKey(),
|
||||
Restriction.RESTRICTED,
|
||||
groupCreatingTenant,
|
||||
privacyGroupId);
|
||||
final String storeTransactionHash = groupCreatingPrivacyNode.execute(storeTransaction);
|
||||
|
||||
groupCreatingPrivacyNode.execute(
|
||||
privacyTransactions.getPrivateTransactionReceipt(storeTransactionHash));
|
||||
|
||||
// check that getting the filter changes works for a member
|
||||
groupCreatingPrivacyNodeBesu.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(tenantToBeRemoved));
|
||||
|
||||
assertThat(
|
||||
groupCreatingPrivacyNode.execute(
|
||||
privacyTransactions.getFilterChanges(privacyGroupId, filterId)))
|
||||
.hasSize(1);
|
||||
|
||||
groupCreatingPrivacyNodeBesu.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(groupCreatingTenant));
|
||||
final CallPrivateSmartContractFunction store2Transaction =
|
||||
privateContractTransactions.callSmartContractWithPrivacyGroupId(
|
||||
eventEmitter.getContractAddress(),
|
||||
eventEmitter.store(BigInteger.valueOf(VALUE_SET)).encodeFunctionCall(),
|
||||
groupCreatingPrivacyNode.getTransactionSigningKey(),
|
||||
Restriction.RESTRICTED,
|
||||
groupCreatingTenant,
|
||||
privacyGroupId);
|
||||
final String store2TransactionHash = groupCreatingPrivacyNode.execute(store2Transaction);
|
||||
|
||||
groupCreatingPrivacyNode.execute(
|
||||
privacyTransactions.getPrivateTransactionReceipt(store2TransactionHash));
|
||||
|
||||
// now remove from privacy group
|
||||
final String removeTransactionHash =
|
||||
removeFromPrivacyGroup(
|
||||
privacyGroupId,
|
||||
groupCreatingPrivacyNode,
|
||||
groupCreatingTenant,
|
||||
Credentials.create(groupCreatingPrivacyNode.getTransactionSigningKey()),
|
||||
tenantToBeRemoved);
|
||||
groupCreatingPrivacyNode.execute(
|
||||
privacyTransactions.getPrivateTransactionReceipt(removeTransactionHash));
|
||||
|
||||
// check that it does not work anymore when member has been removed
|
||||
groupCreatingPrivacyNodeBesu.useAuthenticationTokenInHeaderForJsonRpc(
|
||||
multiTenancyPrivacyNode.getTokenForTenant(tenantToBeRemoved));
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
groupCreatingPrivacyNode.execute(
|
||||
privacyTransactions.getFilterChanges(privacyGroupId, filterId)))
|
||||
.hasMessageContaining("Unauthorized");
|
||||
}
|
||||
|
||||
private String createFlexiblePrivacyGroup(final MultiTenancyPrivacyGroup group) {
|
||||
final List<MultiTenancyPrivacyNode> multiTenancyPrivacyNodes = group.getPrivacyNodes();
|
||||
final MultiTenancyPrivacyNode groupCreatorMultiTenancyPrivacyNode =
|
||||
multiTenancyPrivacyNodes.get(0);
|
||||
final PrivacyNode groupCreatorNode = group.getGroupCreatingPrivacyNode();
|
||||
final String groupCreatorTenant = group.getGroupCreatingTenant();
|
||||
final List<String> members = group.getTenants();
|
||||
final String token = groupCreatorMultiTenancyPrivacyNode.getTokenForTenant(groupCreatorTenant);
|
||||
final CreateFlexiblePrivacyGroupTransaction createTx =
|
||||
privacyTransactions.createFlexiblePrivacyGroup(
|
||||
groupCreatorNode, groupCreatorTenant, members, token);
|
||||
|
||||
final PrivacyRequestFactory.PrivxCreatePrivacyGroupResponse createResponse =
|
||||
groupCreatorNode.execute(createTx);
|
||||
final String privacyGroupId = createResponse.getPrivacyGroupId();
|
||||
|
||||
final List<Base64String> base64StringList =
|
||||
members.stream().map(Base64String::wrap).collect(Collectors.toList());
|
||||
for (final MultiTenancyPrivacyNode mtpn : multiTenancyPrivacyNodes) {
|
||||
final PrivacyNode privacyNode = mtpn.getPrivacyNode();
|
||||
for (final String tenant : mtpn.getTenants()) {
|
||||
if (members.contains(tenant)) {
|
||||
privacyNode
|
||||
.getBesu()
|
||||
.useAuthenticationTokenInHeaderForJsonRpc(mtpn.getTokenForTenant(tenant));
|
||||
privacyNode.verify(flexiblePrivacyGroupExists(privacyGroupId, base64StringList));
|
||||
}
|
||||
}
|
||||
}
|
||||
groupCreatorNode.getBesu().useAuthenticationTokenInHeaderForJsonRpc(token);
|
||||
final String commitmentHash =
|
||||
callGetParticipantsMethodAndReturnCommitmentHash(
|
||||
privacyGroupId, groupCreatorNode, groupCreatorTenant);
|
||||
final PrivateTransactionReceipt expectedReceipt =
|
||||
buildExpectedAddMemberTransactionReceipt(
|
||||
privacyGroupId, groupCreatorNode, groupCreatorTenant, members);
|
||||
|
||||
for (final MultiTenancyPrivacyNode mtpn : multiTenancyPrivacyNodes) {
|
||||
final PrivacyNode privacyNode = mtpn.getPrivacyNode();
|
||||
for (final String tenant : mtpn.getTenants()) {
|
||||
if (members.contains(tenant)) {
|
||||
privacyNode
|
||||
.getBesu()
|
||||
.useAuthenticationTokenInHeaderForJsonRpc(mtpn.getTokenForTenant(tenant));
|
||||
privacyNode.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
commitmentHash, expectedReceipt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return privacyGroupId;
|
||||
}
|
||||
|
||||
private String removeFromPrivacyGroup(
|
||||
final String privacyGroupId,
|
||||
final PrivacyNode node,
|
||||
final String nodeRemovingMember,
|
||||
final Credentials signer,
|
||||
final String memberBeingRemoved) {
|
||||
return node.execute(
|
||||
privacyTransactions.removeFromPrivacyGroup(
|
||||
privacyGroupId, nodeRemovingMember, signer, memberBeingRemoved));
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,11 @@ package org.hyperledger.besu;
|
||||
|
||||
import org.hyperledger.besu.util.platform.PlatformDetector;
|
||||
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Optional;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
/**
|
||||
* Represent Besu information such as version, OS etc. Used with --version option and during Besu
|
||||
@@ -27,6 +31,24 @@ public final class BesuInfo {
|
||||
private static final String VERSION = BesuInfo.class.getPackage().getImplementationVersion();
|
||||
private static final String OS = PlatformDetector.getOS();
|
||||
private static final String VM = PlatformDetector.getVM();
|
||||
private static final String COMMIT;
|
||||
|
||||
static {
|
||||
String className = BesuInfo.class.getSimpleName() + ".class";
|
||||
String classPath = BesuInfo.class.getResource(className).toString();
|
||||
|
||||
String commit;
|
||||
try {
|
||||
URL url = new URL(classPath);
|
||||
JarURLConnection jarConnection = (JarURLConnection) url.openConnection();
|
||||
Manifest manifest = jarConnection.getManifest();
|
||||
Attributes attributes = manifest.getMainAttributes();
|
||||
commit = attributes.getValue("Commit-Hash");
|
||||
} catch (Exception e) {
|
||||
commit = null;
|
||||
}
|
||||
COMMIT = commit;
|
||||
}
|
||||
|
||||
private BesuInfo() {}
|
||||
|
||||
@@ -60,4 +82,13 @@ public final class BesuInfo {
|
||||
.map(identity -> String.format("%s/%s/v%s/%s/%s", CLIENT, identity, VERSION, OS, VM))
|
||||
.orElse(version());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the commit hash for this besu version
|
||||
*
|
||||
* @return the commit hash for this besu version
|
||||
*/
|
||||
public static String commit() {
|
||||
return COMMIT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1291,6 +1291,8 @@ public class RunnerBuilder {
|
||||
new JsonRpcMethodsFactory()
|
||||
.methods(
|
||||
BesuInfo.nodeName(identityString),
|
||||
BesuInfo.shortVersion(),
|
||||
BesuInfo.commit(),
|
||||
ethNetworkConfig.networkId(),
|
||||
besuController.getGenesisConfigOptions(),
|
||||
network,
|
||||
|
||||
@@ -148,10 +148,6 @@ allprojects {
|
||||
url 'https://splunk.jfrog.io/splunk/ext-releases-local'
|
||||
content { includeGroupByRegex('com\\.splunk\\..*') }
|
||||
}
|
||||
maven {
|
||||
url 'https://gitlab.com/api/v4/projects/19871573/packages/maven'
|
||||
content { includeGroupByRegex('com\\.gitlab\\.javafuzz(\\..*)?') }
|
||||
}
|
||||
|
||||
mavenCentral()
|
||||
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ jar {
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash,
|
||||
'Automatic-Module-Name': 'io.consensys.linea-besu.internal.crypto'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ jar {
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash,
|
||||
'Automatic-Module-Name': 'io.consensys.linea-besu.internal.crypto'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ jar {
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash,
|
||||
'Automatic-Module-Name': 'io.consensys.linea-besu.datatypes'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,9 @@ import io.vertx.core.VertxOptions;
|
||||
/** Provides a facade to construct the JSON-RPC component. */
|
||||
public class JsonRpcTestMethodsFactory {
|
||||
|
||||
private static final String CLIENT_VERSION = "TestClientVersion/0.1.0";
|
||||
private static final String CLIENT_NODE_NAME = "TestClientVersion/0.1.0";
|
||||
private static final String CLIENT_VERSION = "0.1.0";
|
||||
private static final String CLIENT_COMMIT = "12345678";
|
||||
private static final BigInteger NETWORK_ID = BigInteger.valueOf(123);
|
||||
|
||||
private final BlockchainImporter importer;
|
||||
@@ -175,7 +177,9 @@ public class JsonRpcTestMethodsFactory {
|
||||
|
||||
return new JsonRpcMethodsFactory()
|
||||
.methods(
|
||||
CLIENT_NODE_NAME,
|
||||
CLIENT_VERSION,
|
||||
CLIENT_COMMIT,
|
||||
NETWORK_ID,
|
||||
new StubGenesisConfigOptions(),
|
||||
peerDiscovery,
|
||||
|
||||
@@ -203,7 +203,7 @@ public class GraphQLDataFetchers {
|
||||
*
|
||||
* <p>The DataFetcher is a functional interface. It has a single method that takes a
|
||||
* DataFetchingEnvironment object as input and returns the maximum priority fee per gas as a Wei
|
||||
* object. If the maximum priority fee per gas is not available, it returns Wei.ZERO.
|
||||
* object.
|
||||
*
|
||||
* @return a DataFetcher that fetches the maximum priority fee per gas of the Ethereum node
|
||||
*/
|
||||
@@ -211,7 +211,7 @@ public class GraphQLDataFetchers {
|
||||
return dataFetchingEnvironment -> {
|
||||
final BlockchainQueries blockchainQuery =
|
||||
dataFetchingEnvironment.getGraphQlContext().get(GraphQLContextType.BLOCKCHAIN_QUERIES);
|
||||
return blockchainQuery.gasPriorityFee().orElse(Wei.ZERO);
|
||||
return blockchainQuery.gasPriorityFee();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ public enum RpcMethod {
|
||||
ENGINE_FORKCHOICE_UPDATED_V2("engine_forkchoiceUpdatedV2"),
|
||||
ENGINE_FORKCHOICE_UPDATED_V3("engine_forkchoiceUpdatedV3"),
|
||||
ENGINE_EXCHANGE_TRANSITION_CONFIGURATION("engine_exchangeTransitionConfigurationV1"),
|
||||
ENGINE_GET_CLIENT_VERSION_V1("engine_getClientVersionV1"),
|
||||
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1("engine_getPayloadBodiesByHashV1"),
|
||||
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1("engine_getPayloadBodiesByRangeV1"),
|
||||
ENGINE_EXCHANGE_CAPABILITIES("engine_exchangeCapabilities"),
|
||||
|
||||
@@ -21,19 +21,13 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcRespon
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
|
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
|
||||
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class EthMaxPriorityFeePerGas implements JsonRpcMethod {
|
||||
|
||||
private final BlockchainQueries blockchainQueries;
|
||||
private final MiningCoordinator miningCoordinator;
|
||||
|
||||
public EthMaxPriorityFeePerGas(
|
||||
final BlockchainQueries blockchainQueries, final MiningCoordinator miningCoordinator) {
|
||||
public EthMaxPriorityFeePerGas(final BlockchainQueries blockchainQueries) {
|
||||
this.blockchainQueries = blockchainQueries;
|
||||
this.miningCoordinator = miningCoordinator;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,7 +42,6 @@ public class EthMaxPriorityFeePerGas implements JsonRpcMethod {
|
||||
}
|
||||
|
||||
private Wei fetchAndLimitPriorityFeePerGas() {
|
||||
final Optional<Wei> gasPrice = blockchainQueries.gasPriorityFee();
|
||||
return gasPrice.orElseGet(miningCoordinator::getMinPriorityFeePerGas);
|
||||
return blockchainQueries.gasPriorityFee();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* 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.api.jsonrpc.internal.methods.engine;
|
||||
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EngineGetClientVersionResultV1;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
|
||||
public class EngineGetClientVersionV1 extends ExecutionEngineJsonRpcMethod {
|
||||
private static final String ENGINE_CLIENT_CODE = "BU";
|
||||
private static final String ENGINE_CLIENT_NAME = "Besu";
|
||||
|
||||
private final String clientVersion;
|
||||
private final String commit;
|
||||
|
||||
public EngineGetClientVersionV1(
|
||||
final Vertx vertx,
|
||||
final ProtocolContext protocolContext,
|
||||
final EngineCallListener engineCallListener,
|
||||
final String clientVersion,
|
||||
final String commit) {
|
||||
super(vertx, protocolContext, engineCallListener);
|
||||
this.clientVersion = clientVersion;
|
||||
this.commit = commit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return RpcMethod.ENGINE_GET_CLIENT_VERSION_V1.getMethodName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonRpcResponse syncResponse(final JsonRpcRequestContext request) {
|
||||
return new JsonRpcSuccessResponse(
|
||||
request.getRequest().getId(),
|
||||
new EngineGetClientVersionResultV1(
|
||||
ENGINE_CLIENT_CODE, ENGINE_CLIENT_NAME, clientVersion, commit.substring(0, 8)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* 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.api.jsonrpc.internal.results;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonGetter;
|
||||
|
||||
public class EngineGetClientVersionResultV1 {
|
||||
private final String code;
|
||||
private final String name;
|
||||
private final String version;
|
||||
private final String commit;
|
||||
|
||||
public EngineGetClientVersionResultV1(
|
||||
final String code, final String name, final String version, final String commit) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.version = version;
|
||||
this.commit = commit;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "code")
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "name")
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "version")
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "commit")
|
||||
public String getCommit() {
|
||||
return commit;
|
||||
}
|
||||
}
|
||||
@@ -185,6 +185,6 @@ public class EthJsonRpcMethods extends ApiGroupJsonRpcMethods {
|
||||
new EthGetMinerDataByBlockHash(blockchainQueries, protocolSchedule),
|
||||
new EthGetMinerDataByBlockNumber(blockchainQueries, protocolSchedule),
|
||||
new EthBlobBaseFee(blockchainQueries.getBlockchain(), protocolSchedule),
|
||||
new EthMaxPriorityFeePerGas(blockchainQueries, miningCoordinator));
|
||||
new EthMaxPriorityFeePerGas(blockchainQueries));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineE
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineForkchoiceUpdatedV1;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineForkchoiceUpdatedV2;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineForkchoiceUpdatedV3;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetClientVersionV1;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadBodiesByHashV1;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadBodiesByRangeV1;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadV1;
|
||||
@@ -57,13 +58,17 @@ public class ExecutionEngineJsonRpcMethods extends ApiGroupJsonRpcMethods {
|
||||
private final ProtocolContext protocolContext;
|
||||
private final EthPeers ethPeers;
|
||||
private final Vertx consensusEngineServer;
|
||||
private final String clientVersion;
|
||||
private final String commit;
|
||||
|
||||
ExecutionEngineJsonRpcMethods(
|
||||
final MiningCoordinator miningCoordinator,
|
||||
final ProtocolSchedule protocolSchedule,
|
||||
final ProtocolContext protocolContext,
|
||||
final EthPeers ethPeers,
|
||||
final Vertx consensusEngineServer) {
|
||||
final Vertx consensusEngineServer,
|
||||
final String clientVersion,
|
||||
final String commit) {
|
||||
this.mergeCoordinator =
|
||||
Optional.ofNullable(miningCoordinator)
|
||||
.filter(mc -> mc.isCompatibleWithEngineApi())
|
||||
@@ -72,6 +77,8 @@ public class ExecutionEngineJsonRpcMethods extends ApiGroupJsonRpcMethods {
|
||||
this.protocolContext = protocolContext;
|
||||
this.ethPeers = ethPeers;
|
||||
this.consensusEngineServer = consensusEngineServer;
|
||||
this.clientVersion = clientVersion;
|
||||
this.commit = commit;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -147,7 +154,9 @@ public class ExecutionEngineJsonRpcMethods extends ApiGroupJsonRpcMethods {
|
||||
new EngineExchangeCapabilities(
|
||||
consensusEngineServer, protocolContext, engineQosTimer),
|
||||
new EnginePreparePayloadDebug(
|
||||
consensusEngineServer, protocolContext, engineQosTimer, mergeCoordinator.get())));
|
||||
consensusEngineServer, protocolContext, engineQosTimer, mergeCoordinator.get()),
|
||||
new EngineGetClientVersionV1(
|
||||
consensusEngineServer, protocolContext, engineQosTimer, clientVersion, commit)));
|
||||
|
||||
if (protocolSchedule.anyMatch(p -> p.spec().getName().equalsIgnoreCase("cancun"))) {
|
||||
executionEngineApisSupported.add(
|
||||
|
||||
@@ -54,7 +54,9 @@ import io.vertx.core.Vertx;
|
||||
public class JsonRpcMethodsFactory {
|
||||
|
||||
public Map<String, JsonRpcMethod> methods(
|
||||
final String clientNodeName,
|
||||
final String clientVersion,
|
||||
final String commit,
|
||||
final BigInteger networkId,
|
||||
final GenesisConfigOptions genesisConfigOptions,
|
||||
final P2PNetwork p2pNetwork,
|
||||
@@ -89,7 +91,7 @@ public class JsonRpcMethodsFactory {
|
||||
final List<JsonRpcMethods> availableApiGroups =
|
||||
List.of(
|
||||
new AdminJsonRpcMethods(
|
||||
clientVersion,
|
||||
clientNodeName,
|
||||
networkId,
|
||||
genesisConfigOptions,
|
||||
p2pNetwork,
|
||||
@@ -115,7 +117,9 @@ public class JsonRpcMethodsFactory {
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
ethPeers,
|
||||
consensusEngineServer),
|
||||
consensusEngineServer,
|
||||
clientVersion,
|
||||
commit),
|
||||
new EthJsonRpcMethods(
|
||||
blockchainQueries,
|
||||
synchronizer,
|
||||
@@ -141,7 +145,7 @@ public class JsonRpcMethodsFactory {
|
||||
filterManager),
|
||||
new PrivxJsonRpcMethods(
|
||||
blockchainQueries, protocolSchedule, transactionPool, privacyParameters),
|
||||
new Web3JsonRpcMethods(clientVersion),
|
||||
new Web3JsonRpcMethods(clientNodeName),
|
||||
new TraceJsonRpcMethods(
|
||||
blockchainQueries, protocolSchedule, protocolContext, apiConfiguration),
|
||||
new TxPoolJsonRpcMethods(transactionPool),
|
||||
|
||||
@@ -46,7 +46,6 @@ import org.hyperledger.besu.evm.log.LogsBloomFilter;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
@@ -1006,7 +1005,7 @@ public class BlockchainQueries {
|
||||
.sorted()
|
||||
.toArray(Wei[]::new);
|
||||
|
||||
return (gasCollection == null || gasCollection.length == 0)
|
||||
return gasCollection.length == 0
|
||||
? gasPriceLowerBound(chainHeadHeader, nextBlockFeeMarket)
|
||||
: UInt256s.max(
|
||||
gasPriceLowerBound(chainHeadHeader, nextBlockFeeMarket),
|
||||
@@ -1045,31 +1044,39 @@ public class BlockchainQueries {
|
||||
return minGasPrice;
|
||||
}
|
||||
|
||||
public Optional<Wei> gasPriorityFee() {
|
||||
final long blockHeight = headBlockNumber();
|
||||
final BigInteger[] gasCollection =
|
||||
LongStream.range(Math.max(0, blockHeight - apiConfig.getGasPriceBlocks()), blockHeight)
|
||||
.mapToObj(
|
||||
l ->
|
||||
blockchain
|
||||
.getBlockByNumber(l)
|
||||
.map(Block::getBody)
|
||||
.map(BlockBody::getTransactions)
|
||||
.orElseThrow(
|
||||
() -> new IllegalStateException("Could not retrieve block #" + l)))
|
||||
public Wei gasPriorityFee() {
|
||||
final Block chainHeadBlock = blockchain.getChainHeadBlock();
|
||||
final long blockHeight = chainHeadBlock.getHeader().getNumber();
|
||||
|
||||
final Wei[] gasCollection =
|
||||
Stream.concat(
|
||||
LongStream.range(
|
||||
Math.max(0, blockHeight - apiConfig.getGasPriceBlocks() + 1), blockHeight)
|
||||
.mapToObj(
|
||||
l ->
|
||||
blockchain
|
||||
.getBlockByNumber(l)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalStateException(
|
||||
"Could not retrieve block #" + l))),
|
||||
Stream.of(chainHeadBlock))
|
||||
.map(Block::getBody)
|
||||
.map(BlockBody::getTransactions)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(t -> t.getMaxPriorityFeePerGas().isPresent())
|
||||
.map(t -> t.getMaxPriorityFeePerGas().get().toBigInteger())
|
||||
.sorted(BigInteger::compareTo)
|
||||
.toArray(BigInteger[]::new);
|
||||
return (gasCollection.length == 0)
|
||||
? Optional.empty()
|
||||
: Optional.of(
|
||||
Wei.of(
|
||||
gasCollection[
|
||||
Math.min(
|
||||
gasCollection.length - 1,
|
||||
(int) ((gasCollection.length) * apiConfig.getGasPriceFraction()))]));
|
||||
.map(t -> t.getMaxPriorityFeePerGas().get())
|
||||
.sorted()
|
||||
.toArray(Wei[]::new);
|
||||
|
||||
return gasCollection.length == 0
|
||||
? miningParameters.getMinPriorityFeePerGas()
|
||||
: UInt256s.max(
|
||||
miningParameters.getMinPriorityFeePerGas(),
|
||||
gasCollection[
|
||||
Math.min(
|
||||
gasCollection.length - 1,
|
||||
(int) ((gasCollection.length) * apiConfig.getGasPriceFraction()))]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.api.graphql;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
|
||||
import org.hyperledger.besu.ethereum.blockcreation.PoWMiningCoordinator;
|
||||
import org.hyperledger.besu.ethereum.core.Synchronizer;
|
||||
@@ -38,7 +42,6 @@ import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
public class GraphQLHttpServiceCorsTest {
|
||||
@TempDir private Path folder;
|
||||
@@ -208,10 +211,11 @@ public class GraphQLHttpServiceCorsTest {
|
||||
config.setCorsAllowedDomains(Lists.newArrayList(corsAllowedDomains));
|
||||
}
|
||||
|
||||
final BlockchainQueries blockchainQueries = Mockito.mock(BlockchainQueries.class);
|
||||
final Synchronizer synchronizer = Mockito.mock(Synchronizer.class);
|
||||
final BlockchainQueries blockchainQueries = mock(BlockchainQueries.class);
|
||||
when(blockchainQueries.gasPriorityFee()).thenReturn(Wei.ONE);
|
||||
final Synchronizer synchronizer = mock(Synchronizer.class);
|
||||
|
||||
final PoWMiningCoordinator miningCoordinatorMock = Mockito.mock(PoWMiningCoordinator.class);
|
||||
final PoWMiningCoordinator miningCoordinatorMock = mock(PoWMiningCoordinator.class);
|
||||
|
||||
// mock graphql context
|
||||
final Map<GraphQLContextType, Object> graphQLContextMap =
|
||||
@@ -219,7 +223,7 @@ public class GraphQLHttpServiceCorsTest {
|
||||
GraphQLContextType.BLOCKCHAIN_QUERIES,
|
||||
blockchainQueries,
|
||||
GraphQLContextType.TRANSACTION_POOL,
|
||||
Mockito.mock(TransactionPool.class),
|
||||
mock(TransactionPool.class),
|
||||
GraphQLContextType.MINING_COORDINATOR,
|
||||
miningCoordinatorMock,
|
||||
GraphQLContextType.SYNCHRONIZER,
|
||||
@@ -233,7 +237,7 @@ public class GraphQLHttpServiceCorsTest {
|
||||
|
||||
final GraphQLHttpService graphQLHttpService =
|
||||
new GraphQLHttpService(
|
||||
vertx, folder, config, graphQL, graphQLContextMap, Mockito.mock(EthScheduler.class));
|
||||
vertx, folder, config, graphQL, graphQLContextMap, mock(EthScheduler.class));
|
||||
graphQLHttpService.start().join();
|
||||
|
||||
return graphQLHttpService;
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.api.graphql;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
|
||||
import org.hyperledger.besu.ethereum.blockcreation.PoWMiningCoordinator;
|
||||
import org.hyperledger.besu.ethereum.core.Synchronizer;
|
||||
@@ -42,7 +46,6 @@ import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
public class GraphQLHttpServiceHostWhitelistTest {
|
||||
|
||||
@@ -69,17 +72,18 @@ public class GraphQLHttpServiceHostWhitelistTest {
|
||||
}
|
||||
|
||||
private GraphQLHttpService createGraphQLHttpService() throws Exception {
|
||||
final BlockchainQueries blockchainQueries = Mockito.mock(BlockchainQueries.class);
|
||||
final Synchronizer synchronizer = Mockito.mock(Synchronizer.class);
|
||||
final BlockchainQueries blockchainQueries = mock(BlockchainQueries.class);
|
||||
when(blockchainQueries.gasPriorityFee()).thenReturn(Wei.ONE);
|
||||
final Synchronizer synchronizer = mock(Synchronizer.class);
|
||||
|
||||
final PoWMiningCoordinator miningCoordinatorMock = Mockito.mock(PoWMiningCoordinator.class);
|
||||
final PoWMiningCoordinator miningCoordinatorMock = mock(PoWMiningCoordinator.class);
|
||||
|
||||
final Map<GraphQLContextType, Object> graphQLContextMap =
|
||||
Map.of(
|
||||
GraphQLContextType.BLOCKCHAIN_QUERIES,
|
||||
blockchainQueries,
|
||||
GraphQLContextType.TRANSACTION_POOL,
|
||||
Mockito.mock(TransactionPool.class),
|
||||
mock(TransactionPool.class),
|
||||
GraphQLContextType.MINING_COORDINATOR,
|
||||
miningCoordinatorMock,
|
||||
GraphQLContextType.SYNCHRONIZER,
|
||||
@@ -92,7 +96,7 @@ public class GraphQLHttpServiceHostWhitelistTest {
|
||||
final GraphQL graphQL = GraphQLProvider.buildGraphQL(dataFetchers);
|
||||
|
||||
return new GraphQLHttpService(
|
||||
vertx, folder, graphQLConfig, graphQL, graphQLContextMap, Mockito.mock(EthScheduler.class));
|
||||
vertx, folder, graphQLConfig, graphQL, graphQLContextMap, mock(EthScheduler.class));
|
||||
}
|
||||
|
||||
private static GraphQLConfiguration createGraphQLConfig() {
|
||||
|
||||
@@ -75,7 +75,9 @@ public abstract class AbstractJsonRpcHttpServiceTest {
|
||||
|
||||
protected BlockchainSetupUtil blockchainSetupUtil;
|
||||
|
||||
protected static String CLIENT_VERSION = "TestClientVersion/0.1.0";
|
||||
protected static final String CLIENT_NODE_NAME = "TestClientVersion/0.1.0";
|
||||
protected static final String CLIENT_VERSION = "0.1.0";
|
||||
protected static final String CLIENT_COMMIT = "12345678";
|
||||
protected static final BigInteger NETWORK_ID = BigInteger.valueOf(123);
|
||||
protected static final Collection<String> JSON_RPC_APIS =
|
||||
Arrays.asList(
|
||||
@@ -168,7 +170,9 @@ public abstract class AbstractJsonRpcHttpServiceTest {
|
||||
|
||||
return new JsonRpcMethodsFactory()
|
||||
.methods(
|
||||
CLIENT_NODE_NAME,
|
||||
CLIENT_VERSION,
|
||||
CLIENT_COMMIT,
|
||||
NETWORK_ID,
|
||||
new StubGenesisConfigOptions(),
|
||||
peerDiscoveryMock,
|
||||
|
||||
@@ -58,13 +58,13 @@ public class AdminJsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
final List<EthPeer> peerList = new ArrayList<>();
|
||||
final PeerInfo info1 =
|
||||
new PeerInfo(
|
||||
4, CLIENT_VERSION, caps, 30302, Bytes.fromHexString(String.format("%0128x", 1)));
|
||||
4, CLIENT_NODE_NAME, caps, 30302, Bytes.fromHexString(String.format("%0128x", 1)));
|
||||
final PeerInfo info2 =
|
||||
new PeerInfo(
|
||||
4, CLIENT_VERSION, caps, 60302, Bytes.fromHexString(String.format("%0128x", 2)));
|
||||
4, CLIENT_NODE_NAME, caps, 60302, Bytes.fromHexString(String.format("%0128x", 2)));
|
||||
final PeerInfo info3 =
|
||||
new PeerInfo(
|
||||
4, CLIENT_VERSION, caps, 60303, Bytes.fromHexString(String.format("%0128x", 3)));
|
||||
4, CLIENT_NODE_NAME, caps, 60303, Bytes.fromHexString(String.format("%0128x", 3)));
|
||||
final InetSocketAddress addr30301 = new InetSocketAddress("localhost", 30301);
|
||||
final InetSocketAddress addr30302 = new InetSocketAddress("localhost", 30302);
|
||||
final InetSocketAddress addr60301 = new InetSocketAddress("localhost", 60301);
|
||||
|
||||
@@ -79,7 +79,9 @@ public class JsonRpcHttpServiceHostAllowlistTest {
|
||||
private static OkHttpClient client;
|
||||
private static String baseUrl;
|
||||
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
private static final String CLIENT_VERSION = "TestClientVersion/0.1.0";
|
||||
private static final String CLIENT_NODE_NAME = "TestClientVersion/0.1.0";
|
||||
private static final String CLIENT_VERSION = "0.1.0";
|
||||
private static final String CLIENT_COMMIT = "12345678";
|
||||
private static final BigInteger CHAIN_ID = BigInteger.valueOf(123);
|
||||
|
||||
private final JsonRpcConfiguration jsonRpcConfig = createJsonRpcConfig();
|
||||
@@ -100,7 +102,9 @@ public class JsonRpcHttpServiceHostAllowlistTest {
|
||||
rpcMethods =
|
||||
new JsonRpcMethodsFactory()
|
||||
.methods(
|
||||
CLIENT_NODE_NAME,
|
||||
CLIENT_VERSION,
|
||||
CLIENT_COMMIT,
|
||||
CHAIN_ID,
|
||||
new StubGenesisConfigOptions(),
|
||||
peerDiscoveryMock,
|
||||
|
||||
@@ -100,7 +100,9 @@ public class JsonRpcHttpServiceLoginTest {
|
||||
protected static OkHttpClient client;
|
||||
protected static String baseUrl;
|
||||
protected static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
protected static final String CLIENT_VERSION = "TestClientVersion/0.1.0";
|
||||
protected static final String CLIENT_NODE_NAME = "TestClientVersion/0.1.0";
|
||||
protected static final String CLIENT_VERSION = "0.1.0";
|
||||
protected static final String CLIENT_COMMIT = "12345678";
|
||||
protected static final BigInteger CHAIN_ID = BigInteger.valueOf(123);
|
||||
protected static P2PNetwork peerDiscoveryMock;
|
||||
protected static BlockchainQueries blockchainQueries;
|
||||
@@ -131,7 +133,9 @@ public class JsonRpcHttpServiceLoginTest {
|
||||
rpcMethods =
|
||||
new JsonRpcMethodsFactory()
|
||||
.methods(
|
||||
CLIENT_NODE_NAME,
|
||||
CLIENT_VERSION,
|
||||
CLIENT_COMMIT,
|
||||
CHAIN_ID,
|
||||
genesisConfigOptions,
|
||||
peerDiscoveryMock,
|
||||
|
||||
@@ -94,7 +94,9 @@ public class JsonRpcHttpServiceRpcApisTest {
|
||||
private JsonRpcHttpService service;
|
||||
private static String baseUrl;
|
||||
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
private static final String CLIENT_VERSION = "TestClientVersion/0.1.0";
|
||||
private static final String CLIENT_NODE_NAME = "TestClientVersion/0.1.0";
|
||||
private static final String CLIENT_VERSION = "0.1.0";
|
||||
private static final String CLIENT_COMMIT = "12345678";
|
||||
private static final BigInteger NETWORK_ID = BigInteger.valueOf(123);
|
||||
private JsonRpcConfiguration configuration;
|
||||
private static final List<String> netServices =
|
||||
@@ -202,7 +204,9 @@ public class JsonRpcHttpServiceRpcApisTest {
|
||||
final Map<String, JsonRpcMethod> rpcMethods =
|
||||
new JsonRpcMethodsFactory()
|
||||
.methods(
|
||||
CLIENT_NODE_NAME,
|
||||
CLIENT_VERSION,
|
||||
CLIENT_COMMIT,
|
||||
NETWORK_ID,
|
||||
new StubGenesisConfigOptions(),
|
||||
mock(P2PNetwork.class),
|
||||
@@ -310,7 +314,9 @@ public class JsonRpcHttpServiceRpcApisTest {
|
||||
final Map<String, JsonRpcMethod> rpcMethods =
|
||||
new JsonRpcMethodsFactory()
|
||||
.methods(
|
||||
CLIENT_NODE_NAME,
|
||||
CLIENT_VERSION,
|
||||
CLIENT_COMMIT,
|
||||
NETWORK_ID,
|
||||
new StubGenesisConfigOptions(),
|
||||
p2pNetwork,
|
||||
|
||||
@@ -202,7 +202,7 @@ public class JsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
testHelper.assertValidJsonRpcResult(json, id);
|
||||
// Check result
|
||||
final String result = json.getString("result");
|
||||
assertThat(result).isEqualTo(CLIENT_VERSION);
|
||||
assertThat(result).isEqualTo(CLIENT_NODE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1127,7 +1127,7 @@ public class JsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
testHelper.assertValidJsonRpcResult(json, id);
|
||||
// Check result
|
||||
final String result = json.getString("result");
|
||||
assertThat(result).isEqualTo(CLIENT_VERSION);
|
||||
assertThat(result).isEqualTo(CLIENT_NODE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1143,7 +1143,7 @@ public class JsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
final JsonObject json = new JsonObject(resp.body().string());
|
||||
testHelper.assertValidJsonRpcResult(json, id);
|
||||
final String result = json.getString("result");
|
||||
assertThat(result).isEqualTo(CLIENT_VERSION);
|
||||
assertThat(result).isEqualTo(CLIENT_NODE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1175,7 +1175,7 @@ public class JsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
testHelper.assertValidJsonRpcResult(json, null);
|
||||
// Check result
|
||||
final String result = json.getString("result");
|
||||
assertThat(result).isEqualTo(CLIENT_VERSION);
|
||||
assertThat(result).isEqualTo(CLIENT_NODE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1197,7 +1197,7 @@ public class JsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
testHelper.assertValidJsonRpcResult(json, id);
|
||||
// Check result
|
||||
final String result = json.getString("result");
|
||||
assertThat(result).isEqualTo(CLIENT_VERSION);
|
||||
assertThat(result).isEqualTo(CLIENT_NODE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1218,7 +1218,7 @@ public class JsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
testHelper.assertValidJsonRpcResult(json, id);
|
||||
// Check result
|
||||
final String result = json.getString("result");
|
||||
assertThat(result).isEqualTo(CLIENT_VERSION);
|
||||
assertThat(result).isEqualTo(CLIENT_NODE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1242,7 +1242,7 @@ public class JsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
testHelper.assertValidJsonRpcResult(json, id);
|
||||
// Check result
|
||||
final String result = json.getString("result");
|
||||
assertThat(result).isEqualTo(CLIENT_VERSION);
|
||||
assertThat(result).isEqualTo(CLIENT_NODE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1268,7 +1268,7 @@ public class JsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
testHelper.assertValidJsonRpcResult(json, id);
|
||||
// Check result
|
||||
final String result = json.getString("result");
|
||||
assertThat(result).isEqualTo(CLIENT_VERSION);
|
||||
assertThat(result).isEqualTo(CLIENT_NODE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1289,7 +1289,7 @@ public class JsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
testHelper.assertValidJsonRpcResult(json, id);
|
||||
// Check result
|
||||
final String result = json.getString("result");
|
||||
assertThat(result).isEqualTo(CLIENT_VERSION);
|
||||
assertThat(result).isEqualTo(CLIENT_NODE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1353,7 +1353,7 @@ public class JsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
final JsonObject json = new JsonObject(resp.body().string());
|
||||
testHelper.assertValidJsonRpcResult(json, id);
|
||||
final String result = json.getString("result");
|
||||
assertThat(result).isEqualTo(CLIENT_VERSION);
|
||||
assertThat(result).isEqualTo(CLIENT_NODE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1485,7 +1485,7 @@ public class JsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
// Check result web3_clientVersion
|
||||
final JsonObject jsonClientVersion = responses.get(clientVersionRequestId);
|
||||
testHelper.assertValidJsonRpcResult(jsonClientVersion, clientVersionRequestId);
|
||||
assertThat(jsonClientVersion.getString("result")).isEqualTo(CLIENT_VERSION);
|
||||
assertThat(jsonClientVersion.getString("result")).isEqualTo(CLIENT_NODE_NAME);
|
||||
|
||||
// Check result unknown method
|
||||
final JsonObject jsonError = responses.get(brokenRequestId);
|
||||
@@ -1540,7 +1540,7 @@ public class JsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
// Check result web3_clientVersion
|
||||
final JsonObject jsonClientVersion = responses.get(clientVersionRequestId);
|
||||
testHelper.assertValidJsonRpcResult(jsonClientVersion, clientVersionRequestId);
|
||||
assertThat(jsonClientVersion.getString("result")).isEqualTo(CLIENT_VERSION);
|
||||
assertThat(jsonClientVersion.getString("result")).isEqualTo(CLIENT_NODE_NAME);
|
||||
|
||||
// Check invalid request
|
||||
final JsonObject jsonError = responses.get(invalidId);
|
||||
@@ -1605,7 +1605,7 @@ public class JsonRpcHttpServiceTest extends JsonRpcHttpServiceTestBase {
|
||||
// Check result web3_clientVersion
|
||||
final JsonObject jsonClientVersion = responses.get(clientVersionRequestId);
|
||||
testHelper.assertValidJsonRpcResult(jsonClientVersion, clientVersionRequestId);
|
||||
assertThat(jsonClientVersion.getString("result")).isEqualTo(CLIENT_VERSION);
|
||||
assertThat(jsonClientVersion.getString("result")).isEqualTo(CLIENT_NODE_NAME);
|
||||
|
||||
// Check result net_version
|
||||
final JsonObject jsonNetVersion = responses.get(netVersionRequestId);
|
||||
|
||||
@@ -78,7 +78,9 @@ public class JsonRpcHttpServiceTestBase {
|
||||
protected static OkHttpClient client;
|
||||
protected static String baseUrl;
|
||||
protected static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
protected static final String CLIENT_VERSION = "TestClientVersion/0.1.0";
|
||||
protected static final String CLIENT_NODE_NAME = "TestClientVersion/0.1.0";
|
||||
protected static final String CLIENT_VERSION = "0.1.0";
|
||||
protected static final String CLIENT_COMMIT = "12345678";
|
||||
protected static final BigInteger CHAIN_ID = BigInteger.valueOf(123);
|
||||
protected static P2PNetwork peerDiscoveryMock;
|
||||
protected static EthPeers ethPeersMock;
|
||||
@@ -108,7 +110,9 @@ public class JsonRpcHttpServiceTestBase {
|
||||
rpcMethods =
|
||||
new JsonRpcMethodsFactory()
|
||||
.methods(
|
||||
CLIENT_NODE_NAME,
|
||||
CLIENT_VERSION,
|
||||
CLIENT_COMMIT,
|
||||
CHAIN_ID,
|
||||
new StubGenesisConfigOptions(),
|
||||
peerDiscoveryMock,
|
||||
|
||||
@@ -85,7 +85,9 @@ public class JsonRpcHttpServiceTlsClientAuthTest {
|
||||
protected static final Vertx vertx = Vertx.vertx();
|
||||
|
||||
private static final String JSON_HEADER = "application/json; charset=utf-8";
|
||||
private static final String CLIENT_VERSION = "TestClientVersion/0.1.0";
|
||||
private static final String CLIENT_NODE_NAME = "TestClientVersion/0.1.0";
|
||||
private static final String CLIENT_VERSION = "0.1.0";
|
||||
private static final String CLIENT_COMMIT = "12345678";
|
||||
private static final BigInteger CHAIN_ID = BigInteger.valueOf(123);
|
||||
|
||||
private static final NatService natService = new NatService(Optional.empty());
|
||||
@@ -114,7 +116,9 @@ public class JsonRpcHttpServiceTlsClientAuthTest {
|
||||
rpcMethods =
|
||||
new JsonRpcMethodsFactory()
|
||||
.methods(
|
||||
CLIENT_NODE_NAME,
|
||||
CLIENT_VERSION,
|
||||
CLIENT_COMMIT,
|
||||
CHAIN_ID,
|
||||
new StubGenesisConfigOptions(),
|
||||
peerDiscoveryMock,
|
||||
|
||||
@@ -75,7 +75,9 @@ class JsonRpcHttpServiceTlsMisconfigurationTest {
|
||||
|
||||
protected static final Vertx vertx = Vertx.vertx();
|
||||
|
||||
private static final String CLIENT_VERSION = "TestClientVersion/0.1.0";
|
||||
private static final String CLIENT_NODE_NAME = "TestClientVersion/0.1.0";
|
||||
private static final String CLIENT_VERSION = "0.1.0";
|
||||
private static final String CLIENT_COMMIT = "12345678";
|
||||
private static final BigInteger CHAIN_ID = BigInteger.valueOf(123);
|
||||
private static final NatService natService = new NatService(Optional.empty());
|
||||
private final SelfSignedP12Certificate besuCertificate = SelfSignedP12Certificate.create();
|
||||
@@ -102,7 +104,9 @@ class JsonRpcHttpServiceTlsMisconfigurationTest {
|
||||
rpcMethods =
|
||||
new JsonRpcMethodsFactory()
|
||||
.methods(
|
||||
CLIENT_NODE_NAME,
|
||||
CLIENT_VERSION,
|
||||
CLIENT_COMMIT,
|
||||
CHAIN_ID,
|
||||
new StubGenesisConfigOptions(),
|
||||
peerDiscoveryMock,
|
||||
|
||||
@@ -81,7 +81,9 @@ public class JsonRpcHttpServiceTlsTest {
|
||||
protected static final Vertx vertx = Vertx.vertx();
|
||||
|
||||
private static final String JSON_HEADER = "application/json; charset=utf-8";
|
||||
private static final String CLIENT_VERSION = "TestClientVersion/0.1.0";
|
||||
private static final String CLIENT_NODE_NAME = "TestClientVersion/0.1.0";
|
||||
private static final String CLIENT_VERSION = "0.1.0";
|
||||
private static final String CLIENT_COMMIT = "12345678";
|
||||
private static final BigInteger CHAIN_ID = BigInteger.valueOf(123);
|
||||
private static final NatService natService = new NatService(Optional.empty());
|
||||
private JsonRpcHttpService service;
|
||||
@@ -103,7 +105,9 @@ public class JsonRpcHttpServiceTlsTest {
|
||||
rpcMethods =
|
||||
new JsonRpcMethodsFactory()
|
||||
.methods(
|
||||
CLIENT_NODE_NAME,
|
||||
CLIENT_VERSION,
|
||||
CLIENT_COMMIT,
|
||||
CHAIN_ID,
|
||||
new StubGenesisConfigOptions(),
|
||||
peerDiscoveryMock,
|
||||
|
||||
@@ -328,7 +328,7 @@ public class EthGasPriceTest {
|
||||
}
|
||||
|
||||
when(blockchain.getChainHeadBlock()).thenReturn(blocksByNumber.get(chainHeadBlockNumber));
|
||||
if (chainHeadBlockNumber > 1) {
|
||||
if (chainHeadBlockNumber > 0) {
|
||||
when(blockchain.getBlockByNumber(anyLong()))
|
||||
.thenAnswer(
|
||||
invocation -> Optional.of(blocksByNumber.get(invocation.getArgument(0, Long.class))));
|
||||
|
||||
@@ -15,29 +15,43 @@
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.api.ImmutableApiConfiguration;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
|
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
|
||||
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.Block;
|
||||
import org.hyperledger.besu.ethereum.core.BlockBody;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Difficulty;
|
||||
import org.hyperledger.besu.ethereum.core.MiningParameters;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.feemarket.CancunFeeMarket;
|
||||
import org.hyperledger.besu.evm.log.LogsBloomFilter;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.internal.verification.VerificationModeFactory;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@@ -45,13 +59,20 @@ public class EthMaxPriorityFeePerGasTest {
|
||||
private static final String JSON_RPC_VERSION = "2.0";
|
||||
private static final String ETH_METHOD =
|
||||
RpcMethod.ETH_GET_MAX_PRIORITY_FEE_PER_GAS.getMethodName();
|
||||
private EthMaxPriorityFeePerGas method;
|
||||
private static final Wei DEFAULT_MIN_PRIORITY_FEE_PER_GAS = Wei.ZERO;
|
||||
private static final long DEFAULT_BLOCK_GAS_LIMIT = 100_000;
|
||||
private static final long DEFAULT_BLOCK_GAS_USED = 21_000;
|
||||
private static final Wei DEFAULT_BASE_FEE = Wei.of(100_000);
|
||||
|
||||
@Mock private BlockchainQueries blockchainQueries;
|
||||
@Mock private MiningCoordinator miningCoordinator;
|
||||
private EthMaxPriorityFeePerGas method;
|
||||
@Mock private ProtocolSchedule protocolSchedule;
|
||||
@Mock private Blockchain blockchain;
|
||||
private MiningParameters miningParameters;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
miningParameters =
|
||||
MiningParameters.newDefault().setMinPriorityFeePerGas(DEFAULT_MIN_PRIORITY_FEE_PER_GAS);
|
||||
method = createEthMaxPriorityFeePerGasMethod();
|
||||
}
|
||||
|
||||
@@ -63,71 +84,156 @@ public class EthMaxPriorityFeePerGasTest {
|
||||
@Test
|
||||
public void whenNoTransactionsExistReturnMinPriorityFeePerGasPrice() {
|
||||
final JsonRpcRequestContext request = requestWithParams();
|
||||
final String expectedWei = Wei.ONE.toShortHexString();
|
||||
final Wei expectedWei = Wei.ONE;
|
||||
miningParameters.setMinPriorityFeePerGas(expectedWei);
|
||||
final JsonRpcResponse expectedResponse =
|
||||
new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei);
|
||||
when(miningCoordinator.getMinPriorityFeePerGas()).thenReturn(Wei.ONE);
|
||||
new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei.toShortHexString());
|
||||
|
||||
mockBlockchainQueriesMaxPriorityFeePerGasPrice(Optional.empty());
|
||||
mockBlockchain(10, 0);
|
||||
final JsonRpcResponse actualResponse = method.response(request);
|
||||
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
|
||||
verify(miningCoordinator, VerificationModeFactory.times(1)).getMinPriorityFeePerGas();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("minPriorityFeePerGasValues")
|
||||
public void whenNoTransactionsExistReturnMinPriorityFeePerGasPriceExist(
|
||||
final Wei minPriorityFeePerGasValue) {
|
||||
final JsonRpcRequestContext request = requestWithParams();
|
||||
final String expectedWei = minPriorityFeePerGasValue.toShortHexString();
|
||||
final JsonRpcResponse expectedResponse =
|
||||
new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei);
|
||||
when(miningCoordinator.getMinPriorityFeePerGas()).thenReturn(minPriorityFeePerGasValue);
|
||||
|
||||
mockBlockchainQueriesMaxPriorityFeePerGasPrice(Optional.empty());
|
||||
final JsonRpcResponse actualResponse = method.response(request);
|
||||
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
|
||||
verify(miningCoordinator, VerificationModeFactory.times(1)).getMinPriorityFeePerGas();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNoTransactionsExistReturnNullMinPriorityFeePerGasPriceExist() {
|
||||
public void whenTransactionsExistReturnMedianMaxPriorityFeePerGasPrice() {
|
||||
final JsonRpcRequestContext request = requestWithParams();
|
||||
final Wei expectedWei = Wei.of(51_000); // max priority fee per gas prices are 1000-100000 wei.
|
||||
final JsonRpcResponse expectedResponse =
|
||||
new JsonRpcSuccessResponse(request.getRequest().getId(), null);
|
||||
when(miningCoordinator.getMinPriorityFeePerGas()).thenReturn(null);
|
||||
new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei.toShortHexString());
|
||||
|
||||
mockBlockchain(100, 1);
|
||||
|
||||
mockBlockchainQueriesMaxPriorityFeePerGasPrice(Optional.empty());
|
||||
final JsonRpcResponse actualResponse = method.response(request);
|
||||
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
|
||||
verify(miningCoordinator, VerificationModeFactory.times(1)).getMinPriorityFeePerGas();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTransactionsExistReturnMaxPriorityFeePerGasPrice() {
|
||||
public void returnMinPriorityFeePerGasWhenMedianValueIsLower() {
|
||||
final JsonRpcRequestContext request = requestWithParams();
|
||||
final String expectedWei = Wei.of(2000000000).toShortHexString();
|
||||
final Wei expectedWei = Wei.of(100_000);
|
||||
miningParameters.setMinPriorityFeePerGas(expectedWei);
|
||||
|
||||
mockBlockchain(100, 1);
|
||||
|
||||
// median value is 51000 wei, that is lower than the value this node is willing to accept,
|
||||
// so the configured min priority fee per gas is returned.
|
||||
final JsonRpcResponse expectedResponse =
|
||||
new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei);
|
||||
mockBlockchainQueriesMaxPriorityFeePerGasPrice(Optional.of(Wei.of(2000000000)));
|
||||
new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei.toShortHexString());
|
||||
|
||||
final JsonRpcResponse actualResponse = method.response(request);
|
||||
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
|
||||
verify(miningCoordinator, VerificationModeFactory.times(0)).getMinPriorityFeePerGas();
|
||||
}
|
||||
|
||||
private static Stream<Arguments> minPriorityFeePerGasValues() {
|
||||
return Stream.of(Arguments.of(Wei.ONE), Arguments.of(Wei.ZERO));
|
||||
}
|
||||
@Test
|
||||
public void atGenesisReturnMinPriorityFeePerGas() {
|
||||
final JsonRpcRequestContext request = requestWithParams();
|
||||
final Wei expectedWei = Wei.ONE;
|
||||
miningParameters.setMinPriorityFeePerGas(expectedWei);
|
||||
final JsonRpcResponse expectedResponse =
|
||||
new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei.toShortHexString());
|
||||
|
||||
private void mockBlockchainQueriesMaxPriorityFeePerGasPrice(final Optional<Wei> result) {
|
||||
when(blockchainQueries.gasPriorityFee()).thenReturn(result);
|
||||
mockBlockchain(0, 0);
|
||||
final JsonRpcResponse actualResponse = method.response(request);
|
||||
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
|
||||
}
|
||||
|
||||
private JsonRpcRequestContext requestWithParams(final Object... params) {
|
||||
return new JsonRpcRequestContext(new JsonRpcRequest(JSON_RPC_VERSION, ETH_METHOD, params));
|
||||
}
|
||||
|
||||
private void mockBlockchain(final long chainHeadBlockNumber, final int txsNum) {
|
||||
final var genesisBaseFee = DEFAULT_BASE_FEE;
|
||||
final var blocksByNumber = new HashMap<Long, Block>();
|
||||
|
||||
final var genesisBlock = createFakeBlock(0, 0, genesisBaseFee);
|
||||
blocksByNumber.put(0L, genesisBlock);
|
||||
|
||||
final var baseFeeMarket = new CancunFeeMarket(0, Optional.empty());
|
||||
|
||||
var baseFee = genesisBaseFee;
|
||||
for (long i = 1; i <= chainHeadBlockNumber; i++) {
|
||||
final var parentHeader = blocksByNumber.get(i - 1).getHeader();
|
||||
baseFee =
|
||||
baseFeeMarket.computeBaseFee(
|
||||
i,
|
||||
parentHeader.getBaseFee().get(),
|
||||
parentHeader.getGasUsed(),
|
||||
parentHeader.getGasLimit());
|
||||
blocksByNumber.put(i, createFakeBlock(i, txsNum, baseFee));
|
||||
}
|
||||
|
||||
when(blockchain.getChainHeadBlock()).thenReturn(blocksByNumber.get(chainHeadBlockNumber));
|
||||
if (chainHeadBlockNumber > 0) {
|
||||
when(blockchain.getBlockByNumber(anyLong()))
|
||||
.thenAnswer(
|
||||
invocation -> Optional.of(blocksByNumber.get(invocation.getArgument(0, Long.class))));
|
||||
}
|
||||
lenient()
|
||||
.when(blockchain.getChainHeadHeader())
|
||||
.thenReturn(blocksByNumber.get(chainHeadBlockNumber).getHeader());
|
||||
}
|
||||
|
||||
private Block createFakeBlock(final long height, final int txsNum, final Wei baseFee) {
|
||||
return createFakeBlock(
|
||||
height, txsNum, baseFee, DEFAULT_BLOCK_GAS_LIMIT, DEFAULT_BLOCK_GAS_USED * txsNum);
|
||||
}
|
||||
|
||||
private Block createFakeBlock(
|
||||
final long height,
|
||||
final int txsNum,
|
||||
final Wei baseFee,
|
||||
final long gasLimit,
|
||||
final long gasUsed) {
|
||||
return new Block(
|
||||
new BlockHeader(
|
||||
Hash.EMPTY,
|
||||
Hash.EMPTY_TRIE_HASH,
|
||||
Address.ZERO,
|
||||
Hash.EMPTY_TRIE_HASH,
|
||||
Hash.EMPTY_TRIE_HASH,
|
||||
Hash.EMPTY_TRIE_HASH,
|
||||
LogsBloomFilter.builder().build(),
|
||||
Difficulty.ONE,
|
||||
height,
|
||||
gasLimit,
|
||||
gasUsed,
|
||||
0,
|
||||
Bytes.EMPTY,
|
||||
baseFee,
|
||||
Hash.EMPTY,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null),
|
||||
new BlockBody(
|
||||
IntStream.range(0, txsNum)
|
||||
.mapToObj(
|
||||
i ->
|
||||
new Transaction.Builder()
|
||||
.chainId(BigInteger.ONE)
|
||||
.type(TransactionType.EIP1559)
|
||||
.nonce(i)
|
||||
.maxFeePerGas(Wei.of(height * 10_000L))
|
||||
.maxPriorityFeePerGas(Wei.of(height * 1_000L))
|
||||
.gasLimit(gasUsed)
|
||||
.value(Wei.ZERO)
|
||||
.build())
|
||||
.toList(),
|
||||
List.of()));
|
||||
}
|
||||
|
||||
private EthMaxPriorityFeePerGas createEthMaxPriorityFeePerGasMethod() {
|
||||
return new EthMaxPriorityFeePerGas(blockchainQueries, miningCoordinator);
|
||||
return new EthMaxPriorityFeePerGas(
|
||||
new BlockchainQueries(
|
||||
protocolSchedule,
|
||||
blockchain,
|
||||
null,
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
ImmutableApiConfiguration.builder().build(),
|
||||
miningParameters));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* 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.api.jsonrpc.internal.methods.engine;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EngineGetClientVersionResultV1;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
class EngineGetClientVersionV1Test {
|
||||
|
||||
private static final String ENGINE_CLIENT_CODE = "BU";
|
||||
private static final String ENGINE_CLIENT_NAME = "Besu";
|
||||
|
||||
private static final String CLIENT_VERSION = "v25.6.7-dev-abcdef12";
|
||||
private static final String COMMIT = "abcdef12";
|
||||
|
||||
private EngineGetClientVersionV1 getClientVersion;
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
getClientVersion =
|
||||
new EngineGetClientVersionV1(
|
||||
Mockito.mock(Vertx.class),
|
||||
Mockito.mock(ProtocolContext.class),
|
||||
Mockito.mock(EngineCallListener.class),
|
||||
CLIENT_VERSION,
|
||||
COMMIT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetName() {
|
||||
assertThat(getClientVersion.getName()).isEqualTo("engine_getClientVersionV1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSyncResponse() {
|
||||
JsonRpcRequestContext request = new JsonRpcRequestContext(new JsonRpcRequest("v", "m", null));
|
||||
JsonRpcResponse actualResult = getClientVersion.syncResponse(request);
|
||||
|
||||
assertThat(actualResult).isInstanceOf(JsonRpcSuccessResponse.class);
|
||||
JsonRpcSuccessResponse successResponse = (JsonRpcSuccessResponse) actualResult;
|
||||
assertThat(successResponse.getResult()).isInstanceOf(EngineGetClientVersionResultV1.class);
|
||||
EngineGetClientVersionResultV1 actualEngineGetClientVersionResultV1 =
|
||||
(EngineGetClientVersionResultV1) successResponse.getResult();
|
||||
assertThat(actualEngineGetClientVersionResultV1.getName()).isEqualTo(ENGINE_CLIENT_NAME);
|
||||
assertThat(actualEngineGetClientVersionResultV1.getCode()).isEqualTo(ENGINE_CLIENT_CODE);
|
||||
assertThat(actualEngineGetClientVersionResultV1.getVersion()).isEqualTo(CLIENT_VERSION);
|
||||
assertThat(actualEngineGetClientVersionResultV1.getCommit()).isEqualTo(COMMIT);
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,9 @@ public class WebSocketServiceLoginTest {
|
||||
protected static OkHttpClient client;
|
||||
protected static String baseUrl;
|
||||
protected static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
protected static final String CLIENT_VERSION = "TestClientVersion/0.1.0";
|
||||
protected static final String CLIENT_NODE_NAME = "TestClientVersion/0.1.0";
|
||||
protected static final String CLIENT_VERSION = "0.1.0";
|
||||
protected static final String CLIENT_COMMIT = "12345678";
|
||||
protected static final BigInteger CHAIN_ID = BigInteger.valueOf(123);
|
||||
protected static P2PNetwork peerDiscoveryMock;
|
||||
protected static BlockchainQueries blockchainQueries;
|
||||
@@ -167,7 +169,9 @@ public class WebSocketServiceLoginTest {
|
||||
spy(
|
||||
new JsonRpcMethodsFactory()
|
||||
.methods(
|
||||
CLIENT_NODE_NAME,
|
||||
CLIENT_VERSION,
|
||||
CLIENT_COMMIT,
|
||||
CHAIN_ID,
|
||||
genesisConfigOptions,
|
||||
peerDiscoveryMock,
|
||||
|
||||
@@ -7,7 +7,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
from(sourceSets.testSupport.output)
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ jar {
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash,
|
||||
'Automatic-Module-Name': 'io.consensys.linea-besu.internal.rlp'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ jar {
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash,
|
||||
'Automatic-Module-Name': 'io.consensys.linea-besu.evm'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -95,6 +95,11 @@ public abstract class AbstractExtCallOperation extends AbstractCallOperation {
|
||||
|
||||
@Override
|
||||
public OperationResult execute(final MessageFrame frame, final EVM evm) {
|
||||
Code callingCode = frame.getCode();
|
||||
if (callingCode.getEofVersion() == 0) {
|
||||
return InvalidOperation.INVALID_RESULT;
|
||||
}
|
||||
|
||||
final Bytes toBytes = frame.getStackItem(STACK_TO).trimLeadingZeros();
|
||||
final Wei value = value(frame);
|
||||
final boolean zeroValue = value.isZero();
|
||||
@@ -141,7 +146,7 @@ public abstract class AbstractExtCallOperation extends AbstractCallOperation {
|
||||
frame.clearReturnData();
|
||||
|
||||
// delegate calls to prior EOF versions are prohibited
|
||||
if (isDelegate() && frame.getCode().getEofVersion() != code.getEofVersion()) {
|
||||
if (isDelegate() && callingCode.getEofVersion() != code.getEofVersion()) {
|
||||
return softFailure(frame, cost);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
/** The Call operation. */
|
||||
public class ExtCallOperation extends AbstractExtCallOperation {
|
||||
|
||||
/** The constant OPCODE. */
|
||||
public static final int OPCODE = 0xF8;
|
||||
|
||||
static final int STACK_VALUE = 3;
|
||||
|
||||
/**
|
||||
@@ -32,7 +35,7 @@ public class ExtCallOperation extends AbstractExtCallOperation {
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public ExtCallOperation(final GasCalculator gasCalculator) {
|
||||
super(0xF8, "EXTCALL", 4, 1, gasCalculator);
|
||||
super(OPCODE, "EXTCALL", 4, 1, gasCalculator);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,13 +24,16 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
/** The Delegate call operation. */
|
||||
public class ExtDelegateCallOperation extends AbstractExtCallOperation {
|
||||
|
||||
/** The constant OPCODE. */
|
||||
public static final int OPCODE = 0xF9;
|
||||
|
||||
/**
|
||||
* Instantiates a new Delegate call operation.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public ExtDelegateCallOperation(final GasCalculator gasCalculator) {
|
||||
super(0xF9, "EXTDELEGATECALL", 3, 1, gasCalculator);
|
||||
super(OPCODE, "EXTDELEGATECALL", 3, 1, gasCalculator);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,13 +24,16 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
/** The Static call operation. */
|
||||
public class ExtStaticCallOperation extends AbstractExtCallOperation {
|
||||
|
||||
/** The constant OPCODE. */
|
||||
public static final int OPCODE = 0xFB;
|
||||
|
||||
/**
|
||||
* Instantiates a new Static call operation.
|
||||
*
|
||||
* @param gasCalculator the gas calculator
|
||||
*/
|
||||
public ExtStaticCallOperation(final GasCalculator gasCalculator) {
|
||||
super(0xFB, "EXTSTATICCALL", 3, 1, gasCalculator);
|
||||
super(OPCODE, "EXTSTATICCALL", 3, 1, gasCalculator);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -45,6 +45,8 @@ public class ExtCallOperationTest {
|
||||
private final WorldUpdater worldUpdater = mock(WorldUpdater.class);
|
||||
private final MutableAccount account = mock(MutableAccount.class);
|
||||
private static final EVM EOF_EVM = MainnetEVMs.pragueEOF(EvmConfiguration.DEFAULT);
|
||||
public static final Code LEGACY_CODE =
|
||||
EOF_EVM.getCodeUncached(Bytes.of(ExtCallOperation.OPCODE, 1));
|
||||
public static final Code SIMPLE_EOF =
|
||||
EOF_EVM.getCodeUncached(Bytes.fromHexString("0xEF00010100040200010001040000000080000000"));
|
||||
public static final Code INVALID_EOF =
|
||||
@@ -118,6 +120,7 @@ public class ExtCallOperationTest {
|
||||
final var messageFrame =
|
||||
new TestMessageFrameBuilder()
|
||||
.initialGas(parentGas)
|
||||
.code(SIMPLE_EOF)
|
||||
.pushStackItem(CONTRACT_ADDRESS) // canary for non-returning
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
@@ -208,6 +211,7 @@ public class ExtCallOperationTest {
|
||||
final var messageFrame =
|
||||
new TestMessageFrameBuilder()
|
||||
.initialGas(parentGas)
|
||||
.code(SIMPLE_EOF)
|
||||
.pushStackItem(CONTRACT_ADDRESS) // canary for non-returning
|
||||
.pushStackItem(valueSent)
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
@@ -243,6 +247,7 @@ public class ExtCallOperationTest {
|
||||
final var messageFrame =
|
||||
new TestMessageFrameBuilder()
|
||||
.initialGas(400000)
|
||||
.code(SIMPLE_EOF)
|
||||
.pushStackItem(CONTRACT_ADDRESS) // canary for non-returning
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
@@ -273,4 +278,33 @@ public class ExtCallOperationTest {
|
||||
assertThat(parentFrame.getStackItem(0))
|
||||
.isEqualTo(AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM);
|
||||
}
|
||||
|
||||
@Test
|
||||
void legacyTest() {
|
||||
final ExtCallOperation operation = new ExtCallOperation(new PragueEOFGasCalculator());
|
||||
|
||||
final var messageFrame =
|
||||
new TestMessageFrameBuilder()
|
||||
.initialGas(400000)
|
||||
.code(LEGACY_CODE)
|
||||
.pushStackItem(CONTRACT_ADDRESS) // canary for non-returning
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
.pushStackItem(CONTRACT_ADDRESS)
|
||||
.worldUpdater(worldUpdater)
|
||||
.build();
|
||||
messageFrame.warmUpAddress(CONTRACT_ADDRESS);
|
||||
when(account.getBalance()).thenReturn(Wei.ZERO);
|
||||
when(account.getCodeHash()).thenReturn(SIMPLE_EOF.getCodeHash());
|
||||
when(account.getCode()).thenReturn(SIMPLE_EOF.getBytes());
|
||||
when(worldUpdater.get(any())).thenReturn(account);
|
||||
when(worldUpdater.getAccount(any())).thenReturn(account);
|
||||
when(worldUpdater.updater()).thenReturn(worldUpdater);
|
||||
|
||||
var result = operation.execute(messageFrame, EOF_EVM);
|
||||
|
||||
assertThat(result.getGasCost()).isZero();
|
||||
assertThat(result.getHaltReason()).isEqualTo(ExceptionalHaltReason.INVALID_OPERATION);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ public class ExtDelegateCallOperationTest {
|
||||
private final MutableAccount account = mock(MutableAccount.class);
|
||||
// private final MutableAccount targetAccount = mock(MutableAccount.class);
|
||||
private static final EVM EOF_EVM = MainnetEVMs.pragueEOF(EvmConfiguration.DEFAULT);
|
||||
public static final Code LEGACY_CODE =
|
||||
EOF_EVM.getCodeUncached(Bytes.of(ExtDelegateCallOperation.OPCODE, 1));
|
||||
public static final Code SIMPLE_EOF =
|
||||
EOF_EVM.getCodeUncached(Bytes.fromHexString("0xEF00010100040200010001040000000080000000"));
|
||||
public static final Code SIMPLE_LEGACY = EOF_EVM.getCodeUncached(Bytes.fromHexString("0x00"));
|
||||
@@ -229,6 +231,7 @@ public class ExtDelegateCallOperationTest {
|
||||
final var messageFrame =
|
||||
new TestMessageFrameBuilder()
|
||||
.initialGas(400000)
|
||||
.code(SIMPLE_EOF)
|
||||
.pushStackItem(CONTRACT_ADDRESS) // canary for non-returning
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
@@ -258,4 +261,34 @@ public class ExtDelegateCallOperationTest {
|
||||
assertThat(parentFrame.getStackItem(0))
|
||||
.isEqualTo(AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM);
|
||||
}
|
||||
|
||||
@Test
|
||||
void legacyTest() {
|
||||
final ExtDelegateCallOperation operation =
|
||||
new ExtDelegateCallOperation(new PragueEOFGasCalculator());
|
||||
|
||||
final var messageFrame =
|
||||
new TestMessageFrameBuilder()
|
||||
.initialGas(400000)
|
||||
.code(LEGACY_CODE)
|
||||
.pushStackItem(CONTRACT_ADDRESS) // canary for non-returning
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
.pushStackItem(CONTRACT_ADDRESS)
|
||||
.worldUpdater(worldUpdater)
|
||||
.build();
|
||||
messageFrame.warmUpAddress(CONTRACT_ADDRESS);
|
||||
when(account.getBalance()).thenReturn(Wei.ZERO);
|
||||
when(account.getCodeHash()).thenReturn(SIMPLE_EOF.getCodeHash());
|
||||
when(account.getCode()).thenReturn(SIMPLE_EOF.getBytes());
|
||||
when(worldUpdater.get(any())).thenReturn(account);
|
||||
when(worldUpdater.getAccount(any())).thenReturn(account);
|
||||
when(worldUpdater.updater()).thenReturn(worldUpdater);
|
||||
|
||||
var result = operation.execute(messageFrame, EOF_EVM);
|
||||
|
||||
assertThat(result.getGasCost()).isZero();
|
||||
assertThat(result.getHaltReason()).isEqualTo(ExceptionalHaltReason.INVALID_OPERATION);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ public class ExtStaticCallOperationTest {
|
||||
private final WorldUpdater worldUpdater = mock(WorldUpdater.class);
|
||||
private final MutableAccount account = mock(MutableAccount.class);
|
||||
private static final EVM EOF_EVM = MainnetEVMs.pragueEOF(EvmConfiguration.DEFAULT);
|
||||
public static final Code LEGACY_CODE =
|
||||
EOF_EVM.getCodeUncached(Bytes.of(ExtStaticCallOperation.OPCODE, 1));
|
||||
public static final Code SIMPLE_EOF =
|
||||
EOF_EVM.getCodeUncached(Bytes.fromHexString("0xEF00010100040200010001040000000080000000"));
|
||||
public static final Code INVALID_EOF =
|
||||
@@ -119,6 +121,7 @@ public class ExtStaticCallOperationTest {
|
||||
final var messageFrame =
|
||||
new TestMessageFrameBuilder()
|
||||
.initialGas(parentGas)
|
||||
.code(SIMPLE_EOF)
|
||||
.pushStackItem(CONTRACT_ADDRESS) // canary for non-returning
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
@@ -154,6 +157,7 @@ public class ExtStaticCallOperationTest {
|
||||
final var messageFrame =
|
||||
new TestMessageFrameBuilder()
|
||||
.initialGas(400000)
|
||||
.code(SIMPLE_EOF)
|
||||
.pushStackItem(CONTRACT_ADDRESS) // canary for non-returning
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
@@ -183,4 +187,34 @@ public class ExtStaticCallOperationTest {
|
||||
assertThat(parentFrame.getStackItem(0))
|
||||
.isEqualTo(AbstractExtCallOperation.EOF1_EXCEPTION_STACK_ITEM);
|
||||
}
|
||||
|
||||
@Test
|
||||
void legacyTest() {
|
||||
final ExtStaticCallOperation operation =
|
||||
new ExtStaticCallOperation(new PragueEOFGasCalculator());
|
||||
|
||||
final var messageFrame =
|
||||
new TestMessageFrameBuilder()
|
||||
.initialGas(400000)
|
||||
.code(LEGACY_CODE)
|
||||
.pushStackItem(CONTRACT_ADDRESS) // canary for non-returning
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
.pushStackItem(Bytes.EMPTY)
|
||||
.pushStackItem(CONTRACT_ADDRESS)
|
||||
.worldUpdater(worldUpdater)
|
||||
.build();
|
||||
messageFrame.warmUpAddress(CONTRACT_ADDRESS);
|
||||
when(account.getBalance()).thenReturn(Wei.ZERO);
|
||||
when(account.getCodeHash()).thenReturn(SIMPLE_EOF.getCodeHash());
|
||||
when(account.getCode()).thenReturn(SIMPLE_EOF.getBytes());
|
||||
when(worldUpdater.get(any())).thenReturn(account);
|
||||
when(worldUpdater.getAccount(any())).thenReturn(account);
|
||||
when(worldUpdater.updater()).thenReturn(worldUpdater);
|
||||
|
||||
var result = operation.execute(messageFrame, EOF_EVM);
|
||||
|
||||
assertThat(result.getGasCost()).isZero();
|
||||
assertThat(result.getHaltReason()).isEqualTo(ExceptionalHaltReason.INVALID_OPERATION);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,19 +546,6 @@
|
||||
<sha256 value="74da05b3ca50a8158101b7e12fbfbf902e011340f14bf31c1776cb51f96147f3" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.gitlab.javafuzz" name="core" version="1.26">
|
||||
<artifact name="core-1.26.jar">
|
||||
<sha256 value="c6c2a7a67fac12db6dd495181082b2cc3fa8fd30399287854119054dde58ba92" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="core-1.26.pom">
|
||||
<sha256 value="e218318c0edfea8c7f7030cbd2ffe9c7db206de39b16147d8a8a2a801515efd6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.gitlab.javafuzz" name="javafuzz" version="1.26">
|
||||
<artifact name="javafuzz-1.26.pom">
|
||||
<sha256 value="c5f521d9795c2bc11293ab08fbc563d453349b398b4fc5afe1388644abc392bf" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google" name="google" version="5">
|
||||
<artifact name="google-5.pom">
|
||||
<sha256 value="e09d345e73ca3fbca7f3e05f30deb74e9d39dd6b79a93fee8c511f23417b6828" origin="Generated by Gradle"/>
|
||||
|
||||
@@ -41,8 +41,6 @@ dependencyManagement {
|
||||
|
||||
dependency 'org.hyperledger.besu:besu-errorprone-checks:1.0.0'
|
||||
|
||||
dependency 'com.gitlab.javafuzz:core:1.26'
|
||||
|
||||
dependency 'com.google.guava:guava:33.0.0-jre'
|
||||
|
||||
dependency 'com.graphql-java:graphql-java:21.5'
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ jar {
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash,
|
||||
'Automatic-Module-Name': 'io.consensys.linea-besu.plugin.api'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ jar {
|
||||
'Specification-Title': archiveBaseName,
|
||||
'Specification-Version': project.version,
|
||||
'Implementation-Title': archiveBaseName,
|
||||
'Implementation-Version': calculateVersion()
|
||||
'Implementation-Version': calculateVersion(),
|
||||
'Commit-Hash': getGitCommitDetails(40).hash
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -38,7 +39,6 @@ dependencies {
|
||||
implementation project(':util')
|
||||
|
||||
implementation 'com.fasterxml.jackson.core:jackson-databind'
|
||||
implementation 'com.gitlab.javafuzz:core'
|
||||
implementation 'com.google.guava:guava'
|
||||
implementation 'info.picocli:picocli'
|
||||
implementation 'io.tmio:tuweni-bytes'
|
||||
@@ -71,6 +71,15 @@ tasks.register("runFuzzer", JavaExec) {
|
||||
}
|
||||
}
|
||||
|
||||
// Adds guidance to the fuzzer but with a 90% performance drop.
|
||||
tasks.register("fuzzGuided") {
|
||||
doLast {
|
||||
runFuzzer.args += "--guidance-regexp=org/(hyperledger/besu|apache/tuweni)"
|
||||
runFuzzer.args += "--new-corpus-dir=${corpusDir}/.."
|
||||
}
|
||||
finalizedBy("runFuzzer")
|
||||
}
|
||||
|
||||
// This fuzzes besu as an external client. Besu fuzzing as a local client is enabled by default.
|
||||
tasks.register("fuzzBesu") {
|
||||
dependsOn(":installDist")
|
||||
@@ -110,7 +119,7 @@ tasks.register("fuzzNethermind") {
|
||||
|
||||
tasks.register("fuzzReth") {
|
||||
doLast {
|
||||
runFuzzer.args += "--client=revm=revme bytecode"
|
||||
runFuzzer.args += "--client=revm=revme bytecode --eof-runtime"
|
||||
}
|
||||
finalizedBy("runFuzzer")
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.MainnetEVMs;
|
||||
import org.hyperledger.besu.evm.internal.EvmConfiguration;
|
||||
import org.hyperledger.besu.testfuzz.javafuzz.Fuzzer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -48,7 +49,6 @@ import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import com.gitlab.javafuzz.core.AbstractFuzzTarget;
|
||||
import com.google.common.base.Stopwatch;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import picocli.CommandLine;
|
||||
@@ -61,7 +61,7 @@ import picocli.CommandLine.Option;
|
||||
description = "Fuzzes EOF container parsing and validation",
|
||||
mixinStandardHelpOptions = true,
|
||||
versionProvider = VersionProvider.class)
|
||||
public class EofContainerSubCommand extends AbstractFuzzTarget implements Runnable {
|
||||
public class EofContainerSubCommand implements Runnable {
|
||||
|
||||
static final String COMMAND_NAME = "eof-container";
|
||||
|
||||
@@ -100,6 +100,16 @@ public class EofContainerSubCommand extends AbstractFuzzTarget implements Runnab
|
||||
description = "Minimum number of fuzz tests before a time limit fuzz error can occur")
|
||||
private long timeThresholdIterations = 2_000;
|
||||
|
||||
@Option(
|
||||
names = {"--guidance-regexp"},
|
||||
description = "Regexp for classes that matter for guidance metric")
|
||||
private String guidanceRegexp;
|
||||
|
||||
@Option(
|
||||
names = {"--new-corpus-dir"},
|
||||
description = "Directory to write hex versions of guidance added contracts")
|
||||
private File newCorpusDir = null;
|
||||
|
||||
@CommandLine.ParentCommand private final BesuFuzzCommand parentCommand;
|
||||
|
||||
static final ObjectMapper eofTestMapper = createObjectMapper();
|
||||
@@ -174,7 +184,13 @@ public class EofContainerSubCommand extends AbstractFuzzTarget implements Runnab
|
||||
System.out.println("Fuzzing client set: " + clients.keySet());
|
||||
|
||||
try {
|
||||
new Fuzzer(this, corpusDir.toString(), this::fuzzStats).start();
|
||||
new Fuzzer(
|
||||
this::parseEOFContainers,
|
||||
corpusDir.toString(),
|
||||
this::fuzzStats,
|
||||
guidanceRegexp,
|
||||
newCorpusDir)
|
||||
.start();
|
||||
} catch (NoSuchAlgorithmException
|
||||
| ClassNotFoundException
|
||||
| InvocationTargetException
|
||||
@@ -212,8 +228,7 @@ public class EofContainerSubCommand extends AbstractFuzzTarget implements Runnab
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fuzz(final byte[] bytes) {
|
||||
void parseEOFContainers(final byte[] bytes) {
|
||||
Bytes eofUnderTest = Bytes.wrap(bytes);
|
||||
String eofUnderTestHexString = eofUnderTest.toHexString();
|
||||
|
||||
@@ -236,7 +251,7 @@ public class EofContainerSubCommand extends AbstractFuzzTarget implements Runnab
|
||||
"%s: slow validation %d µs%n", client.getName(), elapsedMicros);
|
||||
try {
|
||||
Files.writeString(
|
||||
Path.of("slow-" + client.getName() + "-" + name + ".hex"),
|
||||
Path.of("slow-" + name + "-" + client.getName() + ".hex"),
|
||||
eofUnderTestHexString);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* 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.testfuzz.javafuzz;
|
||||
|
||||
import org.hyperledger.besu.crypto.MessageDigestFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.file.Files;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* Ported from <a
|
||||
* href="https://gitlab.com/gitlab-org/security-products/analyzers/fuzzers/javafuzz">...</a> because
|
||||
* fields like max input size were not configurable.
|
||||
*/
|
||||
@SuppressWarnings("CatchAndPrintStackTrace")
|
||||
public class Corpus {
|
||||
private final ArrayList<byte[]> inputs;
|
||||
private final int maxInputSize;
|
||||
private static final int[] INTERESTING8 = {-128, -1, 0, 1, 16, 32, 64, 100, 127};
|
||||
private static final int[] INTERESTING16 = {
|
||||
-32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767, -128, -1, 0, 1, 16, 32, 64, 100, 127
|
||||
};
|
||||
private static final int[] INTERESTING32 = {
|
||||
-2147483648,
|
||||
-100663046,
|
||||
-32769,
|
||||
32768,
|
||||
65535,
|
||||
65536,
|
||||
100663045,
|
||||
2147483647,
|
||||
-32768,
|
||||
-129,
|
||||
128,
|
||||
255,
|
||||
256,
|
||||
512,
|
||||
1000,
|
||||
1024,
|
||||
4096,
|
||||
32767,
|
||||
-128,
|
||||
-1,
|
||||
0,
|
||||
1,
|
||||
16,
|
||||
32,
|
||||
64,
|
||||
100,
|
||||
127
|
||||
};
|
||||
private String corpusPath;
|
||||
private int seedLength;
|
||||
|
||||
/**
|
||||
* Create a corpus
|
||||
*
|
||||
* @param dirs The directory to store the corpus files
|
||||
*/
|
||||
public Corpus(final String dirs) {
|
||||
this.maxInputSize = 0xc001; // 48k+1
|
||||
this.corpusPath = null;
|
||||
this.inputs = new ArrayList<>();
|
||||
if (dirs != null) {
|
||||
String[] arr = dirs.split(",", -1);
|
||||
for (String s : arr) {
|
||||
File f = new File(s);
|
||||
if (!f.exists()) {
|
||||
f.mkdirs();
|
||||
}
|
||||
if (f.isDirectory()) {
|
||||
if (this.corpusPath == null) {
|
||||
this.corpusPath = f.getPath();
|
||||
}
|
||||
this.loadDir(f);
|
||||
} else {
|
||||
try {
|
||||
this.inputs.add(Files.readAllBytes(f.toPath()));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.seedLength = this.inputs.size();
|
||||
}
|
||||
|
||||
int getLength() {
|
||||
return this.inputs.size();
|
||||
}
|
||||
|
||||
private boolean randBool() {
|
||||
return ThreadLocalRandom.current().nextBoolean();
|
||||
}
|
||||
|
||||
private int rand(final int max) {
|
||||
return ThreadLocalRandom.current().nextInt(0, max);
|
||||
}
|
||||
|
||||
private void loadDir(final File dir) {
|
||||
for (final File f : dir.listFiles()) {
|
||||
if (f.isFile()) {
|
||||
try {
|
||||
this.inputs.add(Files.readAllBytes(f.toPath()));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte[] generateInput() throws NoSuchAlgorithmException {
|
||||
if (this.seedLength != 0) {
|
||||
this.seedLength--;
|
||||
return this.inputs.get(this.seedLength);
|
||||
}
|
||||
if (this.inputs.isEmpty()) {
|
||||
byte[] buf = new byte[] {};
|
||||
this.putBuffer(buf);
|
||||
return buf;
|
||||
}
|
||||
byte[] buf = this.inputs.get(this.rand(this.inputs.size()));
|
||||
return this.mutate(buf);
|
||||
}
|
||||
|
||||
void putBuffer(final byte[] buf) throws NoSuchAlgorithmException {
|
||||
if (this.inputs.contains(buf)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.inputs.add(buf);
|
||||
|
||||
writeCorpusFile(buf);
|
||||
}
|
||||
|
||||
private void writeCorpusFile(final byte[] buf) throws NoSuchAlgorithmException {
|
||||
if (this.corpusPath != null) {
|
||||
MessageDigest md = MessageDigestFactory.create("SHA-256");
|
||||
md.update(buf);
|
||||
byte[] digest = md.digest();
|
||||
String hex = String.format("%064x", new BigInteger(1, digest));
|
||||
try (FileOutputStream fos = new FileOutputStream(this.corpusPath + "/" + hex)) {
|
||||
fos.write(buf);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String dec2bin(final int dec) {
|
||||
String bin = Integer.toBinaryString(dec);
|
||||
String padding = new String(new char[32 - bin.length()]).replace("\0", "0");
|
||||
return padding + bin;
|
||||
}
|
||||
|
||||
private int exp2() {
|
||||
String bin = dec2bin(this.rand((int) Math.pow(2, 32)));
|
||||
int count = 0;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (bin.charAt(i) == '0') {
|
||||
count++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int chooseLen(final int n) {
|
||||
int x = this.rand(100);
|
||||
if (x < 90) {
|
||||
return this.rand(Math.min(8, n)) + 1;
|
||||
} else if (x < 99) {
|
||||
return this.rand(Math.min(32, n)) + 1;
|
||||
} else {
|
||||
return this.rand(n) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void copy(
|
||||
final byte[] src, final int srcPos, final byte[] dst, final int dstPos, final int length) {
|
||||
System.arraycopy(src, srcPos, dst, dstPos, Math.min(length, src.length - srcPos));
|
||||
}
|
||||
|
||||
static void copy(final byte[] src, final int srcPos, final byte[] dst, final int dstPos) {
|
||||
System.arraycopy(src, srcPos, dst, dstPos, Math.min(src.length - srcPos, dst.length - dstPos));
|
||||
}
|
||||
|
||||
static byte[] concatZeros(final byte[] a, final int n) {
|
||||
byte[] c = new byte[a.length + n];
|
||||
Arrays.fill(c, (byte) 0);
|
||||
System.arraycopy(a, 0, c, 0, a.length);
|
||||
return c;
|
||||
}
|
||||
|
||||
byte[] mutate(final byte[] buf) {
|
||||
byte[] res = buf.clone();
|
||||
int nm = 1 + this.exp2();
|
||||
for (int i = 0; i < nm; i++) {
|
||||
int x = this.rand(16);
|
||||
if (x == 0) {
|
||||
// Remove a range of bytes.
|
||||
if (res.length <= 1) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
int pos0 = this.rand(res.length);
|
||||
int pos1 = pos0 + this.chooseLen(res.length - pos0);
|
||||
copy(res, pos1, res, pos0, res.length - pos0);
|
||||
res = Arrays.copyOfRange(res, 0, res.length - (pos1 - pos0));
|
||||
} else if (x == 1) {
|
||||
// Insert a range of random bytes.
|
||||
int pos = this.rand(res.length + 1);
|
||||
int n = this.chooseLen(10);
|
||||
res = concatZeros(res, n);
|
||||
copy(res, pos, res, pos + n);
|
||||
for (int k = 0; k < n; k++) {
|
||||
res[pos + k] = (byte) this.rand(256);
|
||||
}
|
||||
} else if (x == 2) {
|
||||
// Duplicate a range of bytes.
|
||||
if (res.length <= 1) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
int src = this.rand(res.length);
|
||||
int dst = this.rand(res.length);
|
||||
while (src == dst) {
|
||||
dst = this.rand(res.length);
|
||||
}
|
||||
int n = this.chooseLen(res.length - src);
|
||||
byte[] tmp = new byte[n];
|
||||
Arrays.fill(tmp, (byte) 0);
|
||||
copy(res, src, tmp, 0);
|
||||
res = concatZeros(res, n);
|
||||
copy(res, dst, res, dst + n);
|
||||
System.arraycopy(tmp, 0, res, dst, n);
|
||||
} else if (x == 3) {
|
||||
// Copy a range of bytes.
|
||||
if (res.length <= 1) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
int src = this.rand(res.length);
|
||||
int dst = this.rand(res.length);
|
||||
while (src == dst) {
|
||||
dst = this.rand(res.length);
|
||||
}
|
||||
int n = this.chooseLen(res.length - src);
|
||||
copy(res, src + n, res, dst);
|
||||
} else if (x == 4) {
|
||||
// Bit flip. Spooky!
|
||||
if (res.length <= 1) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
int pos = this.rand(res.length);
|
||||
res[pos] ^= (byte) (1 << (byte) this.rand(8));
|
||||
} else if (x == 5) {
|
||||
// Set a byte to a random value.
|
||||
if (res.length <= 1) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
int pos = this.rand(res.length);
|
||||
res[pos] ^= (byte) (this.rand(255) + 1);
|
||||
} else if (x == 6) {
|
||||
// Swap 2 bytes.
|
||||
if (res.length <= 1) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
int src = this.rand(res.length);
|
||||
int dst = this.rand(res.length);
|
||||
while (src == dst) {
|
||||
dst = this.rand(res.length);
|
||||
}
|
||||
byte tmp1 = res[src];
|
||||
res[src] = res[dst];
|
||||
res[dst] = tmp1;
|
||||
} else if (x == 7) {
|
||||
// Add/subtract from a byte.
|
||||
if (res.length == 0) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
int pos = this.rand(res.length);
|
||||
int v = this.rand(35) + 1;
|
||||
if (this.randBool()) {
|
||||
res[pos] += (byte) v;
|
||||
} else {
|
||||
res[pos] -= (byte) v;
|
||||
}
|
||||
} else if (x == 8) {
|
||||
// Add/subtract from a uint16.
|
||||
i--;
|
||||
// if (res.length < 2) {
|
||||
// i--;
|
||||
// continue;
|
||||
// }
|
||||
// int pos = this.rand(res.length - 1);
|
||||
// int v = this.rand(35) + 1;
|
||||
// if (this.randBool()) {
|
||||
// v = 0 - v;
|
||||
// }
|
||||
//
|
||||
// if (this.randBool()) {
|
||||
// res[pos] =
|
||||
// } else {
|
||||
//
|
||||
// }
|
||||
|
||||
} else if (x == 9) {
|
||||
i--;
|
||||
// Add/subtract from a uint32.
|
||||
} else if (x == 10) {
|
||||
// Replace a byte with an interesting value.
|
||||
if (res.length == 0) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
int pos = this.rand(res.length);
|
||||
res[pos] = (byte) INTERESTING8[this.rand(INTERESTING8.length)];
|
||||
} else if (x == 11) {
|
||||
// Replace an uint16 with an interesting value.
|
||||
if (res.length < 2) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
int pos = this.rand(res.length - 1);
|
||||
if (this.randBool()) {
|
||||
res[pos] = (byte) (INTERESTING16[this.rand(INTERESTING16.length)] & 0xFF);
|
||||
res[pos + 1] = (byte) ((INTERESTING16[this.rand(INTERESTING16.length)] >> 8) & 0xFF);
|
||||
} else {
|
||||
res[pos + 1] = (byte) (INTERESTING16[this.rand(INTERESTING16.length)] & 0xFF);
|
||||
res[pos] = (byte) ((INTERESTING16[this.rand(INTERESTING16.length)] >> 8) & 0xFF);
|
||||
}
|
||||
} else if (x == 12) {
|
||||
// Replace an uint32 with an interesting value.
|
||||
if (res.length < 4) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
int pos = this.rand(res.length - 3);
|
||||
if (this.randBool()) {
|
||||
res[pos] = (byte) (INTERESTING32[this.rand(INTERESTING32.length)] & 0xFF);
|
||||
res[pos + 1] = (byte) ((INTERESTING32[this.rand(INTERESTING32.length)] >> 8) & 0xFF);
|
||||
res[pos + 2] = (byte) ((INTERESTING32[this.rand(INTERESTING32.length)] >> 16) & 0xFF);
|
||||
res[pos + 3] = (byte) ((INTERESTING32[this.rand(INTERESTING32.length)] >> 24) & 0xFF);
|
||||
} else {
|
||||
res[pos + 3] = (byte) (INTERESTING32[this.rand(INTERESTING32.length)] & 0xFF);
|
||||
res[pos + 2] = (byte) ((INTERESTING32[this.rand(INTERESTING32.length)] >> 8) & 0xFF);
|
||||
res[pos + 1] = (byte) ((INTERESTING32[this.rand(INTERESTING32.length)] >> 16) & 0xFF);
|
||||
res[pos] = (byte) ((INTERESTING32[this.rand(INTERESTING32.length)] >> 24) & 0xFF);
|
||||
}
|
||||
} else if (x == 13) {
|
||||
// Replace an ascii digit with another digit.
|
||||
List<Integer> digits = new ArrayList<>();
|
||||
for (int k = 0; k < res.length; k++) {
|
||||
if (res[k] >= 48 && res[k] <= 57) {
|
||||
digits.add(k);
|
||||
}
|
||||
}
|
||||
if (digits.isEmpty()) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
int pos = this.rand(digits.size());
|
||||
int was = res[digits.get(pos)];
|
||||
int now = was;
|
||||
while (now == was) {
|
||||
now = this.rand(10) + 48;
|
||||
}
|
||||
res[digits.get(pos)] = (byte) now;
|
||||
} else if (x == 14) {
|
||||
// Splice another input.
|
||||
if (res.length < 4 || this.inputs.size() < 2) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
byte[] other = this.inputs.get(this.rand(this.inputs.size()));
|
||||
if (other.length < 4) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
// Find common prefix and suffix.
|
||||
int idx0 = 0;
|
||||
while (idx0 < res.length && idx0 < other.length && res[idx0] == other[idx0]) {
|
||||
idx0++;
|
||||
}
|
||||
int idx1 = 0;
|
||||
while (idx1 < res.length
|
||||
&& idx1 < other.length
|
||||
&& res[res.length - idx1 - 1] == other[other.length - idx1 - 1]) {
|
||||
idx1++;
|
||||
}
|
||||
int diff = Math.min(res.length - idx0 - idx1, other.length - idx0 - idx1);
|
||||
if (diff < 4) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
copy(other, idx0, res, idx0, Math.min(other.length, idx0 + this.rand(diff - 2) + 1) - idx0);
|
||||
} else if (x == 15) {
|
||||
// Insert a part of another input.
|
||||
if (res.length < 4 || this.inputs.size() < 2) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
byte[] other = this.inputs.get(this.rand(this.inputs.size()));
|
||||
if (other.length < 4) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
int pos0 = this.rand(res.length + 1);
|
||||
int pos1 = this.rand(other.length - 2);
|
||||
int n = this.chooseLen(other.length - pos1 - 2) + 2;
|
||||
res = concatZeros(res, n);
|
||||
copy(res, pos0, res, pos0 + n);
|
||||
if (n >= 0) System.arraycopy(other, pos1, res, pos0, n);
|
||||
}
|
||||
}
|
||||
|
||||
if (res.length > this.maxInputSize) {
|
||||
res = Arrays.copyOfRange(res, 0, this.maxInputSize);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* 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.testfuzz.javafuzz;
|
||||
|
||||
/**
|
||||
* Adapted from <a
|
||||
* href="https://gitlab.com/gitlab-org/security-products/analyzers/fuzzers/javafuzz">...</a> because
|
||||
* I wanted it to be a functional interface
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface FuzzTarget {
|
||||
|
||||
/**
|
||||
* The target to fuzz
|
||||
*
|
||||
* @param data data proviced by the fuzzer
|
||||
*/
|
||||
void fuzz(byte[] data);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user