mirror of https://github.com/hyperledger/besu
[Privacy] Allow users to provide a private genesis (#2509)
* Refactor: PrivacyBlockProcessor to clarify intent Signed-off-by: Antony Denyer <git@antonydenyer.co.uk> * Wire up privacy genesis options into PrivacyParameters Signed-off-by: Antony Denyer <git@antonydenyer.co.uk> * Refactor private state genesis into it's own class - pass through in privacyParameters Signed-off-by: Antony Denyer <git@antonydenyer.co.uk> * Refactor: inject PrivateStateGenesis into PrivacyPrecompiles Signed-off-by: Antony Denyer <git@antonydenyer.co.uk> * Private Genesis initialisation - set code, balance and storage from private-genesis.json Signed-off-by: Antony Denyer <git@antonydenyer.co.uk> * Check on-chain with private genesis in acceptance tests Signed-off-by: Antony Denyer <git@antonydenyer.co.uk> * Remove unused EthGetCodeCall Signed-off-by: Antony Denyer <git@antonydenyer.co.uk> * Use a plugin based aproach for privacy genesis - if the plugin is registered it will be used to apply private genesis state - if onchain flexible privacy groups is enable that will be applied after Signed-off-by: Antony Denyer <git@antonydenyer.co.uk> * PrivateGenesisAcceptanceTest::createPrivacyGroup can be private Signed-off-by: Antony Denyer <git@antonydenyer.co.uk> * PrivateStateGenesis add debug logs Signed-off-by: Antony Denyer <git@antonydenyer.co.uk> * Warn if genesis account allocation is in reserved precompile range Signed-off-by: Antony Denyer <git@antonydenyer.co.uk> * Add sender into unsignedPrivateMarkerTransaction for plugin to make descision Signed-off-by: Antony Denyer <git@antonydenyer.co.uk> * Remove locking solution as may not be needed - if this is required we should first evaluate actual use cases and test scenarios Signed-off-by: Antony Denyer <git@antonydenyer.co.uk> * Rename PrivateStateGenesis -> PrivateStateGenesisAllocator Signed-off-by: Antony Denyer <git@antonydenyer.co.uk> * Tidy up naming for getPrivateStateGenesisAllocator Signed-off-by: Antony Denyer <git@antonydenyer.co.uk> * Privacy Plugin javadocs Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>pull/2623/head
parent
047b680ddc
commit
19c34514a1
@ -0,0 +1,87 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.tests.acceptance.dsl.privacy.contract; |
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; |
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.math.BigInteger; |
||||
|
||||
import org.web3j.crypto.Credentials; |
||||
import org.web3j.protocol.Web3j; |
||||
import org.web3j.tx.Contract; |
||||
import org.web3j.tx.TransactionManager; |
||||
import org.web3j.tx.gas.BesuPrivacyGasProvider; |
||||
import org.web3j.tx.gas.ContractGasProvider; |
||||
import org.web3j.utils.Base64String; |
||||
|
||||
public class LoadPrivateSmartContractTransactionWithPrivacyGroupId<T extends Contract> |
||||
implements Transaction<T> { |
||||
|
||||
private static final BesuPrivacyGasProvider GAS_PROVIDER = |
||||
new BesuPrivacyGasProvider(BigInteger.valueOf(1000)); |
||||
private static final Object METHOD_IS_STATIC = null; |
||||
|
||||
private final Class<T> clazz; |
||||
private final Credentials senderCredentials; |
||||
private final Base64String privateFrom; |
||||
private final Base64String privacyGroupId; |
||||
private final String contractAddress; |
||||
|
||||
public LoadPrivateSmartContractTransactionWithPrivacyGroupId( |
||||
final String contractAddress, |
||||
final Class<T> clazz, |
||||
final String transactionSigningKey, |
||||
final String privateFrom, |
||||
final String privacyGroupId) { |
||||
|
||||
this.contractAddress = contractAddress; |
||||
this.clazz = clazz; |
||||
this.senderCredentials = Credentials.create(transactionSigningKey); |
||||
this.privateFrom = Base64String.wrap(privateFrom); |
||||
this.privacyGroupId = Base64String.wrap(privacyGroupId); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
@Override |
||||
public T execute(final NodeRequests node) { |
||||
final PrivateTransactionManager privateTransactionManager = |
||||
new PrivateTransactionManager.Builder( |
||||
node.privacy().getBesuClient(), senderCredentials, privateFrom) |
||||
.setPrivacyGroupId(privacyGroupId) |
||||
.build(); |
||||
|
||||
try { |
||||
final Method method = |
||||
clazz.getMethod( |
||||
"load", |
||||
String.class, |
||||
Web3j.class, |
||||
TransactionManager.class, |
||||
ContractGasProvider.class); |
||||
|
||||
return (T) |
||||
method.invoke( |
||||
METHOD_IS_STATIC, |
||||
contractAddress, |
||||
node.privacy().getBesuClient(), |
||||
privateTransactionManager, |
||||
GAS_PROVIDER); |
||||
} catch (final Exception e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,87 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
* |
||||
*/ |
||||
package org.hyperledger.besu.plugins.privacy; |
||||
|
||||
import org.hyperledger.besu.ethereum.core.Wei; |
||||
import org.hyperledger.besu.plugin.data.Address; |
||||
import org.hyperledger.besu.plugin.data.PrivacyGenesis; |
||||
import org.hyperledger.besu.plugin.data.PrivacyGenesisAccount; |
||||
import org.hyperledger.besu.plugin.data.Quantity; |
||||
import org.hyperledger.besu.plugin.services.privacy.PrivacyGroupGenesisProvider; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.apache.tuweni.units.bigints.UInt256; |
||||
|
||||
public class TestPrivacyGroupGenesisProvider implements PrivacyGroupGenesisProvider { |
||||
private boolean genesisEnabled = false; |
||||
|
||||
public void setGenesisEnabled() { |
||||
this.genesisEnabled = true; |
||||
} |
||||
|
||||
@Override |
||||
public PrivacyGenesis getPrivacyGenesis(final Bytes privacyGroupId, final long blockNumber) { |
||||
if (!genesisEnabled) return Collections::emptyList; |
||||
|
||||
return () -> |
||||
List.of( |
||||
new PrivacyGenesisAccount() { |
||||
@Override |
||||
public Address getAddress() { |
||||
return org.hyperledger.besu.ethereum.core.Address.fromHexString( |
||||
"0x1000000000000000000000000000000000000001"); |
||||
} |
||||
|
||||
@Override |
||||
public Map<UInt256, UInt256> getStorage() { |
||||
return Collections.emptyMap(); |
||||
} |
||||
|
||||
@Override |
||||
public int getVersion() { |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
public Long getNonce() { |
||||
return 0L; |
||||
} |
||||
|
||||
@Override |
||||
public Quantity getBalance() { |
||||
return Wei.of(1000); |
||||
} |
||||
|
||||
// The code in the genesis file needs to be the deployed contract code, not the code
|
||||
// to deploy
|
||||
// the contract
|
||||
// you can generate it using the solc --bin-runtime flag
|
||||
// cd ./acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/
|
||||
// docker run -v $PWD:/sources ethereum/solc:0.5.0 -o /sources/output --bin-runtime
|
||||
// /sources/EventEmitter.sol --overwrite
|
||||
|
||||
@Override |
||||
public Bytes getCode() { |
||||
return Bytes.fromHexString( |
||||
"0x608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633fa4f2451461005c5780636057361d1461008757806367e404ce146100c2575b600080fd5b34801561006857600080fd5b50610071610119565b6040518082815260200191505060405180910390f35b34801561009357600080fd5b506100c0600480360360208110156100aa57600080fd5b8101908080359060200190929190505050610123565b005b3480156100ce57600080fd5b506100d76101d9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6000600254905090565b7fc9db20adedc6cf2b5d25252b101ab03e124902a73fcb12b753f3d1aaa2d8f9f53382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a18060028190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690509056fea165627a7a72305820e74360c3d08936cb1747ad641729261ff5e83b6fc0d303d136e171f15f07d7740029"); |
||||
} |
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,62 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
* |
||||
*/ |
||||
package org.hyperledger.besu.plugins.privacy; |
||||
|
||||
import static org.hyperledger.besu.ethereum.privacy.PrivateTransaction.readFrom; |
||||
import static org.hyperledger.besu.ethereum.privacy.PrivateTransaction.serialize; |
||||
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; |
||||
import org.hyperledger.besu.plugin.data.PrivateTransaction; |
||||
import org.hyperledger.besu.plugin.data.Transaction; |
||||
import org.hyperledger.besu.plugin.services.privacy.PrivacyPluginPayloadProvider; |
||||
|
||||
import java.util.Optional; |
||||
|
||||
import org.apache.logging.log4j.LogManager; |
||||
import org.apache.logging.log4j.Logger; |
||||
import org.apache.tuweni.bytes.Bytes; |
||||
|
||||
public class TestPrivacyPluginPayloadProvider implements PrivacyPluginPayloadProvider { |
||||
private static final Logger LOG = LogManager.getLogger(); |
||||
private String prefix; |
||||
|
||||
public void setPluginPayloadPrefix(final String prefix) { |
||||
this.prefix = prefix; |
||||
} |
||||
|
||||
@Override |
||||
public Bytes generateMarkerPayload( |
||||
final PrivateTransaction privateTransaction, final String privacyUserId) { |
||||
|
||||
return Bytes.wrap(Bytes.fromHexString(prefix), serialize(privateTransaction).encoded()); |
||||
} |
||||
|
||||
@Override |
||||
public Optional<PrivateTransaction> getPrivateTransactionFromPayload( |
||||
final Transaction transaction) { |
||||
|
||||
final Bytes prefixBytes = Bytes.fromHexString(prefix); |
||||
if (transaction.getPayload().slice(0, prefixBytes.size()).equals(prefixBytes)) { |
||||
LOG.info("processing payload for " + prefix); |
||||
final BytesValueRLPInput bytesValueRLPInput = |
||||
new BytesValueRLPInput(transaction.getPayload().slice(prefixBytes.size()).copy(), false); |
||||
return Optional.of(readFrom(bytesValueRLPInput)); |
||||
} else { |
||||
LOG.info("Can not process payload for " + prefix); |
||||
return Optional.empty(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,95 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.tests.acceptance.privacy; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.web3j.utils.Restriction.RESTRICTED; |
||||
import static org.web3j.utils.Restriction.UNRESTRICTED; |
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.ParameterizedEnclaveTestBase; |
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyNode; |
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.account.PrivacyAccountResolver; |
||||
import org.hyperledger.besu.tests.web3j.generated.EventEmitter; |
||||
import org.hyperledger.enclave.testutil.EnclaveType; |
||||
|
||||
import java.io.IOException; |
||||
import java.math.BigInteger; |
||||
import java.util.Optional; |
||||
|
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.junit.runners.Parameterized; |
||||
import org.web3j.protocol.core.methods.response.EthCall; |
||||
import org.web3j.utils.Restriction; |
||||
|
||||
@RunWith(Parameterized.class) |
||||
public class PrivateGenesisAcceptanceTest extends ParameterizedEnclaveTestBase { |
||||
private final PrivacyNode alice; |
||||
|
||||
public PrivateGenesisAcceptanceTest(final Restriction restriction, final EnclaveType enclaveType) |
||||
throws IOException { |
||||
|
||||
super(restriction, enclaveType); |
||||
|
||||
alice = |
||||
privacyBesu.createIbft2NodePrivacyEnabledWithGenesis( |
||||
"node1", |
||||
PrivacyAccountResolver.ALICE, |
||||
true, |
||||
enclaveType, |
||||
Optional.empty(), |
||||
false, |
||||
false, |
||||
restriction == UNRESTRICTED, |
||||
"AA"); |
||||
|
||||
privacyCluster.start(alice); |
||||
} |
||||
|
||||
@Test |
||||
public void canInteractWithPrivateGenesisPreCompile() throws Exception { |
||||
final String privacyGroupId = createPrivacyGroup(); |
||||
|
||||
final EventEmitter eventEmitter = |
||||
alice.execute( |
||||
privateContractTransactions.loadSmartContractWithPrivacyGroupId( |
||||
"0x1000000000000000000000000000000000000001", |
||||
EventEmitter.class, |
||||
alice.getTransactionSigningKey(), |
||||
alice.getEnclaveKey(), |
||||
privacyGroupId)); |
||||
|
||||
eventEmitter.store(BigInteger.valueOf(42)).send(); |
||||
|
||||
final EthCall response = |
||||
alice.execute( |
||||
privacyTransactions.privCall( |
||||
privacyGroupId, eventEmitter, eventEmitter.value().encodeFunctionCall())); |
||||
|
||||
final String value = response.getValue(); |
||||
|
||||
assertThat(new BigInteger(value.substring(2), 16)).isEqualByComparingTo(BigInteger.valueOf(42)); |
||||
} |
||||
|
||||
private String createPrivacyGroup() { |
||||
if (restriction == RESTRICTED) { |
||||
return alice.execute(privacyTransactions.createPrivacyGroup("name", "description", alice)); |
||||
} else if (restriction == UNRESTRICTED) { |
||||
return "gsvwYfGPurL7wgXKmgFtCamXarAl9fA5jaSXi8TLpJw="; |
||||
} else { |
||||
throw new RuntimeException("Do not know how to handle " + restriction); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,113 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.privacy; |
||||
|
||||
import org.hyperledger.besu.ethereum.core.Address; |
||||
import org.hyperledger.besu.ethereum.core.MutableAccount; |
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState; |
||||
import org.hyperledger.besu.ethereum.core.Wei; |
||||
import org.hyperledger.besu.ethereum.core.WorldUpdater; |
||||
import org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement; |
||||
import org.hyperledger.besu.plugin.data.PrivacyGenesis; |
||||
import org.hyperledger.besu.plugin.services.privacy.PrivacyGroupGenesisProvider; |
||||
|
||||
import java.math.BigInteger; |
||||
|
||||
import org.apache.logging.log4j.LogManager; |
||||
import org.apache.logging.log4j.Logger; |
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.apache.tuweni.bytes.Bytes32; |
||||
import org.apache.tuweni.units.bigints.UInt256; |
||||
|
||||
public class PrivateStateGenesisAllocator { |
||||
private static final Logger LOG = LogManager.getLogger(); |
||||
|
||||
private final Boolean isOnchainPrivacyEnabled; |
||||
private final PrivacyGroupGenesisProvider privacyGroupGenesisProvider; |
||||
|
||||
public PrivateStateGenesisAllocator( |
||||
final Boolean isOnchainPrivacyEnabled, |
||||
final PrivacyGroupGenesisProvider privacyGroupGenesisProvider) { |
||||
this.isOnchainPrivacyEnabled = isOnchainPrivacyEnabled; |
||||
this.privacyGroupGenesisProvider = privacyGroupGenesisProvider; |
||||
} |
||||
|
||||
public void applyGenesisToPrivateWorldState( |
||||
final MutableWorldState disposablePrivateState, |
||||
final WorldUpdater privateWorldStateUpdater, |
||||
final Bytes privacyGroupId, |
||||
final long blockNumber) { |
||||
|
||||
final PrivacyGenesis genesis = |
||||
privacyGroupGenesisProvider.getPrivacyGenesis(privacyGroupId, blockNumber); |
||||
|
||||
if (genesis.getAccounts().size() > 0) { |
||||
|
||||
LOG.debug( |
||||
"Applying {} privacy accounts onto {} private state genesis at {}", |
||||
genesis.getAccounts().size(), |
||||
privacyGroupId, |
||||
blockNumber); |
||||
|
||||
genesis |
||||
.getAccounts() |
||||
.forEach( |
||||
(genesisAccount) -> { |
||||
final Address address = Address.fromPlugin(genesisAccount.getAddress()); |
||||
if (address.toBigInteger().compareTo(BigInteger.valueOf(Byte.MAX_VALUE)) < 0) { |
||||
LOG.warn( |
||||
"Genesis address {} is in reserved range and may be overwritten", address); |
||||
} |
||||
|
||||
final MutableAccount account = |
||||
privateWorldStateUpdater.createAccount(address).getMutable(); |
||||
|
||||
LOG.debug("{} applied to genesis", address.toHexString()); |
||||
|
||||
account.setNonce(genesisAccount.getNonce()); |
||||
account.setBalance(Wei.fromQuantity(genesisAccount.getBalance())); |
||||
account.setCode(genesisAccount.getCode()); |
||||
account.setVersion(genesisAccount.getVersion()); |
||||
|
||||
genesisAccount.getStorage().forEach(account::setStorageValue); |
||||
}); |
||||
} |
||||
|
||||
if (isOnchainPrivacyEnabled) { |
||||
// inject management
|
||||
final MutableAccount managementContract = |
||||
privateWorldStateUpdater |
||||
.createAccount(Address.DEFAULT_ONCHAIN_PRIVACY_MANAGEMENT) |
||||
.getMutable(); |
||||
|
||||
// this is the code for the simple management contract
|
||||
managementContract.setCode(OnChainGroupManagement.DEFAULT_GROUP_MANAGEMENT_RUNTIME_BYTECODE); |
||||
|
||||
// inject proxy
|
||||
final MutableAccount procyContract = |
||||
privateWorldStateUpdater.createAccount(Address.ONCHAIN_PRIVACY_PROXY).getMutable(); |
||||
|
||||
// this is the code for the proxy contract
|
||||
procyContract.setCode(OnChainGroupManagement.PROXY_RUNTIME_BYTECODE); |
||||
// manually set the management contract address so the proxy can trust it
|
||||
procyContract.setStorageValue( |
||||
UInt256.ZERO, |
||||
UInt256.fromBytes(Bytes32.leftPad(Address.DEFAULT_ONCHAIN_PRIVACY_MANAGEMENT))); |
||||
} |
||||
|
||||
privateWorldStateUpdater.commit(); |
||||
disposablePrivateState.persist(null); |
||||
} |
||||
} |
@ -0,0 +1,160 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
package org.hyperledger.besu.ethereum.privacy; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.hyperledger.besu.ethereum.core.Address.DEFAULT_ONCHAIN_PRIVACY_MANAGEMENT; |
||||
import static org.hyperledger.besu.ethereum.core.Address.ONCHAIN_PRIVACY_PROXY; |
||||
import static org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement.DEFAULT_GROUP_MANAGEMENT_RUNTIME_BYTECODE; |
||||
import static org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement.PROXY_RUNTIME_BYTECODE; |
||||
|
||||
import org.hyperledger.besu.ethereum.core.Account; |
||||
import org.hyperledger.besu.ethereum.core.Address; |
||||
import org.hyperledger.besu.ethereum.core.Hash; |
||||
import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider; |
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState; |
||||
import org.hyperledger.besu.ethereum.core.Wei; |
||||
import org.hyperledger.besu.ethereum.core.WorldUpdater; |
||||
import org.hyperledger.besu.ethereum.trie.MerklePatriciaTrie; |
||||
import org.hyperledger.besu.plugin.data.PrivacyGenesis; |
||||
import org.hyperledger.besu.plugin.data.PrivacyGenesisAccount; |
||||
import org.hyperledger.besu.plugin.data.Quantity; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.apache.tuweni.units.bigints.UInt256; |
||||
import org.junit.Test; |
||||
|
||||
public class PrivateStateGenesisAllocatorTest { |
||||
public static final Hash EMPTY_ROOT_HASH = Hash.wrap(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH); |
||||
|
||||
private final MutableWorldState worldState = |
||||
InMemoryKeyValueStorageProvider.createInMemoryWorldState(); |
||||
|
||||
final WorldUpdater updater = worldState.updater(); |
||||
|
||||
Address genesisAddress = Address.fromHexString("0x1000000000000000000000000000000000000001"); |
||||
|
||||
PrivacyGenesis privacyGenesis = |
||||
() -> |
||||
List.of( |
||||
new PrivacyGenesisAccount() { |
||||
@Override |
||||
public org.hyperledger.besu.plugin.data.Address getAddress() { |
||||
return genesisAddress; |
||||
} |
||||
|
||||
@Override |
||||
public Map<UInt256, UInt256> getStorage() { |
||||
return Collections.emptyMap(); |
||||
} |
||||
|
||||
@Override |
||||
public int getVersion() { |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
public Long getNonce() { |
||||
return 0L; |
||||
} |
||||
|
||||
@Override |
||||
public Quantity getBalance() { |
||||
return Wei.ONE; |
||||
} |
||||
|
||||
@Override |
||||
public Bytes getCode() { |
||||
return Bytes.fromHexString("0x42"); |
||||
} |
||||
}); |
||||
|
||||
@Test |
||||
public void whenOnChainDisabledAndNoAccountsProvidedNoGenesisIsApplied() { |
||||
PrivateStateGenesisAllocator privateStateGenesisAllocator = |
||||
new PrivateStateGenesisAllocator( |
||||
false, (privacyGroupId, blockNumber) -> Collections::emptyList); |
||||
|
||||
privateStateGenesisAllocator.applyGenesisToPrivateWorldState( |
||||
worldState, updater, Bytes.EMPTY, 0); |
||||
|
||||
assertThat(worldState.frontierRootHash()).isEqualTo(EMPTY_ROOT_HASH); |
||||
} |
||||
|
||||
@Test |
||||
public void whenOnChainEnabledAndNoAccountsProvidedPrivacyManagementContractIsApplied() { |
||||
PrivateStateGenesisAllocator privateStateGenesisAllocator = |
||||
new PrivateStateGenesisAllocator( |
||||
true, (privacyGroupId, blockNumber) -> Collections::emptyList); |
||||
|
||||
privateStateGenesisAllocator.applyGenesisToPrivateWorldState( |
||||
worldState, updater, Bytes.EMPTY, 0); |
||||
|
||||
assertThat(worldState.frontierRootHash()).isNotEqualTo(EMPTY_ROOT_HASH); |
||||
|
||||
assertManagementContractApplied(); |
||||
} |
||||
|
||||
@Test |
||||
public void whenOnChainEnabledAndAccountsProvidedPrivacyManagementContractAndGenesisIsApplied() { |
||||
PrivateStateGenesisAllocator privateStateGenesisAllocator = |
||||
new PrivateStateGenesisAllocator(true, (privacyGroupId, blockNumber) -> privacyGenesis); |
||||
|
||||
privateStateGenesisAllocator.applyGenesisToPrivateWorldState( |
||||
worldState, updater, Bytes.EMPTY, 0); |
||||
|
||||
assertThat(worldState.frontierRootHash()).isNotEqualTo(EMPTY_ROOT_HASH); |
||||
|
||||
assertManagementContractApplied(); |
||||
assertGenesisAccountApplied(); |
||||
} |
||||
|
||||
@Test |
||||
public void whenOnChainDisabledAndAccountsProvidedPrivacyManagementContractAndGenesisIsApplied() { |
||||
PrivateStateGenesisAllocator privateStateGenesisAllocator = |
||||
new PrivateStateGenesisAllocator(false, (privacyGroupId, blockNumber) -> privacyGenesis); |
||||
|
||||
privateStateGenesisAllocator.applyGenesisToPrivateWorldState( |
||||
worldState, updater, Bytes.EMPTY, 0); |
||||
|
||||
assertThat(worldState.frontierRootHash()).isNotEqualTo(EMPTY_ROOT_HASH); |
||||
|
||||
assertThat(worldState.get(ONCHAIN_PRIVACY_PROXY)).isEqualTo(null); |
||||
|
||||
assertGenesisAccountApplied(); |
||||
} |
||||
|
||||
private void assertGenesisAccountApplied() { |
||||
Account genesisAccount = worldState.get(genesisAddress); |
||||
|
||||
assertThat(genesisAccount.getCode()).isEqualTo(Bytes.fromHexString("0x42")); |
||||
assertThat(genesisAccount.getBalance()).isEqualTo(Wei.ONE); |
||||
} |
||||
|
||||
private void assertManagementContractApplied() { |
||||
Account managementProxy = worldState.get(ONCHAIN_PRIVACY_PROXY); |
||||
assertThat(managementProxy.getCode()).isEqualTo(PROXY_RUNTIME_BYTECODE); |
||||
assertThat(managementProxy.getStorageValue(UInt256.ZERO)) |
||||
.isEqualTo(UInt256.fromBytes(DEFAULT_ONCHAIN_PRIVACY_MANAGEMENT)); |
||||
|
||||
Account managementContract = worldState.get(DEFAULT_ONCHAIN_PRIVACY_MANAGEMENT); |
||||
assertThat(managementContract.getCode()).isEqualTo(DEFAULT_GROUP_MANAGEMENT_RUNTIME_BYTECODE); |
||||
} |
||||
} |
@ -0,0 +1,27 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.plugin.data; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** An interface to return the private state genesis. */ |
||||
public interface PrivacyGenesis { |
||||
/** |
||||
* A list of pre-compiled wallets with associated information. |
||||
* |
||||
* @return The list of addresses, possibly empty. Should never be null. |
||||
*/ |
||||
List<PrivacyGenesisAccount> getAccounts(); |
||||
} |
@ -0,0 +1,66 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.plugin.data; |
||||
|
||||
import java.util.Map; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.apache.tuweni.units.bigints.UInt256; |
||||
|
||||
public interface PrivacyGenesisAccount { |
||||
/** |
||||
* The address of the initial genesis allocation/pre-compile |
||||
* |
||||
* @return Ethereum account address |
||||
*/ |
||||
Address getAddress(); |
||||
|
||||
/** |
||||
* Any storage that the address requires |
||||
* |
||||
* @return account storage |
||||
*/ |
||||
Map<UInt256, UInt256> getStorage(); |
||||
|
||||
/** |
||||
* The version for the account. |
||||
* |
||||
* @return the version of the code being set |
||||
*/ |
||||
int getVersion(); |
||||
|
||||
/** |
||||
* The initial nonce assigned to the account. |
||||
* |
||||
* @return the nonce |
||||
*/ |
||||
Long getNonce(); |
||||
|
||||
/** |
||||
* The initial balance assigned to the account. |
||||
* |
||||
* @return the balance |
||||
*/ |
||||
Quantity getBalance(); |
||||
|
||||
/** |
||||
* The initial code for the account. |
||||
* |
||||
* <p>Note! this must be the contract code and not the code to deploy the contract. |
||||
* |
||||
* @return the wallet code. Can be null or 0x |
||||
*/ |
||||
Bytes getCode(); |
||||
} |
@ -0,0 +1,32 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.plugin.services.privacy; |
||||
|
||||
import org.hyperledger.besu.plugin.data.PrivacyGenesis; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
|
||||
/** A way to initiate private state with a genesis */ |
||||
@FunctionalInterface |
||||
public interface PrivacyGroupGenesisProvider { |
||||
/** |
||||
* Allows you to specify a custom private genesis to apply when initialising a privacy group |
||||
* |
||||
* @param privacyGroupId the privacyGroupId |
||||
* @param blockNumber the block height |
||||
* @return the privacy genesis to apply |
||||
*/ |
||||
PrivacyGenesis getPrivacyGenesis(Bytes privacyGroupId, long blockNumber); |
||||
} |
Loading…
Reference in new issue