diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyAcceptanceTestBase.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyAcceptanceTestBase.java index 1a3f258bfc..3fbb116b27 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyAcceptanceTestBase.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyAcceptanceTestBase.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.tests.acceptance.dsl.privacy.condition.PrivateContra 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.net.NetTransactions; import org.junit.After; @@ -37,7 +38,7 @@ public class PrivacyAcceptanceTestBase { protected final PrivateContractTransactions privateContractTransactions; protected final PrivacyCluster privacyCluster; protected final PrivacyAccountResolver privacyAccountResolver; - + protected final ContractTransactions contractTransactions; protected final NetConditions net; public PrivacyAcceptanceTestBase() { @@ -49,6 +50,7 @@ public class PrivacyAcceptanceTestBase { privateContractTransactions = new PrivateContractTransactions(); privacyCluster = new PrivacyCluster(net); privacyAccountResolver = new PrivacyAccountResolver(); + contractTransactions = new ContractTransactions(); } @After diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/CrossContractReader.sol b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/CrossContractReader.sol new file mode 100644 index 0000000000..32c6836433 --- /dev/null +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/CrossContractReader.sol @@ -0,0 +1,60 @@ +/* + * Copyright 2019 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. + */ +pragma solidity >=0.4.0 <0.6.0; + +import "./EventEmitter.sol"; + +// compile with: +// solc CrossContractReader.sol --bin --abi --optimize --overwrite -o . +// then create web3j wrappers with: +// web3j solidity generate -b ./generated/CrossContractReader.bin -a ./generated/CrossContractReader.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated +contract CrossContractReader { + uint counter; + + event NewEventEmitter( + address contractAddress + ); + + function read(address emitter_address) view public returns (uint) { + EventEmitter em = EventEmitter(emitter_address); + return em.value(); + } + + function deploy() public { + EventEmitter em = new EventEmitter(); + emit NewEventEmitter(address(em)); + } + + function deployRemote(address crossAddress) public { + CrossContractReader cross = CrossContractReader(crossAddress); + cross.deploy(); + } + + function increment() public { + counter++; + } + + function incrementRemote(address crossAddress) public { + CrossContractReader cross = CrossContractReader(crossAddress); + cross.increment(); + } + + function destroy() public { + selfdestruct(msg.sender); + } + + function remoteDestroy(address crossAddress) public { + CrossContractReader cross = CrossContractReader(crossAddress); + cross.destroy(); + } +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/generated/CrossContractReader.abi b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/generated/CrossContractReader.abi new file mode 100644 index 0000000000..36edfd6c59 --- /dev/null +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/generated/CrossContractReader.abi @@ -0,0 +1 @@ +[{"constant":false,"inputs":[{"name":"crossAddress","type":"address"}],"name":"remoteDestroy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"crossAddress","type":"address"}],"name":"deployRemote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deploy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"destroy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"emitter_address","type":"address"}],"name":"read","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"increment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"crossAddress","type":"address"}],"name":"incrementRemote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"contractAddress","type":"address"}],"name":"NewEventEmitter","type":"event"}] \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/generated/CrossContractReader.bin b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/generated/CrossContractReader.bin new file mode 100644 index 0000000000..a18bc7505c --- /dev/null +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/generated/CrossContractReader.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506104b7806100206000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806383197ef01161005b57806383197ef0146100d8578063a087a87e146100e0578063d09de08a14610118578063e689ef8a146101205761007d565b8063305155f9146100825780635374ded2146100aa578063775c300c146100d0575b600080fd5b6100a86004803603602081101561009857600080fd5b50356001600160a01b0316610146565b005b6100a8600480360360208110156100c057600080fd5b50356001600160a01b03166101a2565b6100a86101e2565b6100a8610250565b610106600480360360208110156100f657600080fd5b50356001600160a01b0316610253565b60408051918252519081900360200190f35b6100a86102c5565b6100a86004803603602081101561013657600080fd5b50356001600160a01b03166102d0565b6000819050806001600160a01b03166383197ef06040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561018657600080fd5b505af115801561019a573d6000803e3d6000fd5b505050505050565b6000819050806001600160a01b031663775c300c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561018657600080fd5b60006040516101f090610310565b604051809103906000f08015801561020c573d6000803e3d6000fd5b50604080516001600160a01b038316815290519192507f9ac6876e0aa40667ffeaa9b359b5ed924f4cdd0e029eb6e9c369e78c68f711fb919081900360200190a150565b33ff5b600080829050806001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561029257600080fd5b505afa1580156102a6573d6000803e3d6000fd5b505050506040513d60208110156102bc57600080fd5b50519392505050565b600080546001019055565b6000819050806001600160a01b031663d09de08a6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561018657600080fd5b6101658061031e8339019056fe608060405234801561001057600080fd5b50600080546001600160a01b03191633179055610133806100326000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80633fa4f2451460415780636057361d14605957806367e404ce146075575b600080fd5b60476097565b60408051918252519081900360200190f35b607360048036036020811015606d57600080fd5b5035609d565b005b607b60ef565b604080516001600160a01b039092168252519081900360200190f35b60025490565b604080513381526020810183905281517fc9db20adedc6cf2b5d25252b101ab03e124902a73fcb12b753f3d1aaa2d8f9f5929181900390910190a1600255600180546001600160a01b03191633179055565b6001546001600160a01b03169056fea265627a7a72305820dc1ce4d08260105d146ec5efa5274950ee9e66f81ff18994d44a40fbd33e45c064736f6c634300050a0032a265627a7a72305820d71e5a225a48fdeb043aaba4264138353b3443a28658bacec7570e108659ad2864736f6c634300050a0032 \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/generated/CrossContractReader.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/generated/CrossContractReader.java new file mode 100644 index 0000000000..9ce0c0fafd --- /dev/null +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/generated/CrossContractReader.java @@ -0,0 +1,276 @@ +/* + * 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.web3j.generated; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import io.reactivex.Flowable; +import org.web3j.abi.EventEncoder; +import org.web3j.abi.TypeReference; +import org.web3j.abi.datatypes.Address; +import org.web3j.abi.datatypes.Event; +import org.web3j.abi.datatypes.Function; +import org.web3j.abi.datatypes.Type; +import org.web3j.abi.datatypes.generated.Uint256; +import org.web3j.crypto.Credentials; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.core.DefaultBlockParameter; +import org.web3j.protocol.core.RemoteCall; +import org.web3j.protocol.core.RemoteFunctionCall; +import org.web3j.protocol.core.methods.request.EthFilter; +import org.web3j.protocol.core.methods.response.BaseEventResponse; +import org.web3j.protocol.core.methods.response.Log; +import org.web3j.protocol.core.methods.response.TransactionReceipt; +import org.web3j.tx.Contract; +import org.web3j.tx.TransactionManager; +import org.web3j.tx.gas.ContractGasProvider; + +/** + * Auto generated code. + * + *

Do not modify! + * + *

Please use the web3j command line tools, + * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the codegen module to update. + * + *

Generated with web3j version 4.5.0. + */ +@SuppressWarnings("rawtypes") +public class CrossContractReader extends Contract { + private static final String BINARY = + "608060405234801561001057600080fd5b506104b7806100206000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806383197ef01161005b57806383197ef0146100d8578063a087a87e146100e0578063d09de08a14610118578063e689ef8a146101205761007d565b8063305155f9146100825780635374ded2146100aa578063775c300c146100d0575b600080fd5b6100a86004803603602081101561009857600080fd5b50356001600160a01b0316610146565b005b6100a8600480360360208110156100c057600080fd5b50356001600160a01b03166101a2565b6100a86101e2565b6100a8610250565b610106600480360360208110156100f657600080fd5b50356001600160a01b0316610253565b60408051918252519081900360200190f35b6100a86102c5565b6100a86004803603602081101561013657600080fd5b50356001600160a01b03166102d0565b6000819050806001600160a01b03166383197ef06040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561018657600080fd5b505af115801561019a573d6000803e3d6000fd5b505050505050565b6000819050806001600160a01b031663775c300c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561018657600080fd5b60006040516101f090610310565b604051809103906000f08015801561020c573d6000803e3d6000fd5b50604080516001600160a01b038316815290519192507f9ac6876e0aa40667ffeaa9b359b5ed924f4cdd0e029eb6e9c369e78c68f711fb919081900360200190a150565b33ff5b600080829050806001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561029257600080fd5b505afa1580156102a6573d6000803e3d6000fd5b505050506040513d60208110156102bc57600080fd5b50519392505050565b600080546001019055565b6000819050806001600160a01b031663d09de08a6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561018657600080fd5b6101658061031e8339019056fe608060405234801561001057600080fd5b50600080546001600160a01b03191633179055610133806100326000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80633fa4f2451460415780636057361d14605957806367e404ce146075575b600080fd5b60476097565b60408051918252519081900360200190f35b607360048036036020811015606d57600080fd5b5035609d565b005b607b60ef565b604080516001600160a01b039092168252519081900360200190f35b60025490565b604080513381526020810183905281517fc9db20adedc6cf2b5d25252b101ab03e124902a73fcb12b753f3d1aaa2d8f9f5929181900390910190a1600255600180546001600160a01b03191633179055565b6001546001600160a01b03169056fea265627a7a72305820dc1ce4d08260105d146ec5efa5274950ee9e66f81ff18994d44a40fbd33e45c064736f6c634300050a0032a265627a7a72305820d71e5a225a48fdeb043aaba4264138353b3443a28658bacec7570e108659ad2864736f6c634300050a0032"; + + public static final String FUNC_REMOTEDESTROY = "remoteDestroy"; + + public static final String FUNC_DEPLOYREMOTE = "deployRemote"; + + public static final String FUNC_DESTROY = "destroy"; + + public static final String FUNC_READ = "read"; + + public static final String FUNC_INCREMENT = "increment"; + + public static final String FUNC_INCREMENTREMOTE = "incrementRemote"; + + public static final Event NEWEVENTEMITTER_EVENT = + new Event( + "NewEventEmitter", Arrays.>asList(new TypeReference

() {}));; + + @Deprecated + protected CrossContractReader( + String contractAddress, + Web3j web3j, + Credentials credentials, + BigInteger gasPrice, + BigInteger gasLimit) { + super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit); + } + + protected CrossContractReader( + String contractAddress, + Web3j web3j, + Credentials credentials, + ContractGasProvider contractGasProvider) { + super(BINARY, contractAddress, web3j, credentials, contractGasProvider); + } + + @Deprecated + protected CrossContractReader( + String contractAddress, + Web3j web3j, + TransactionManager transactionManager, + BigInteger gasPrice, + BigInteger gasLimit) { + super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit); + } + + protected CrossContractReader( + String contractAddress, + Web3j web3j, + TransactionManager transactionManager, + ContractGasProvider contractGasProvider) { + super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider); + } + + public RemoteFunctionCall remoteDestroy(String crossAddress) { + final Function function = + new Function( + FUNC_REMOTEDESTROY, + Arrays.asList(new org.web3j.abi.datatypes.Address(160, crossAddress)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall deployRemote(String crossAddress) { + final Function function = + new Function( + FUNC_DEPLOYREMOTE, + Arrays.asList(new org.web3j.abi.datatypes.Address(160, crossAddress)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall deploy() { + final Function function = + new Function(FUNC_DEPLOY, Arrays.asList(), Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall destroy() { + final Function function = + new Function( + FUNC_DESTROY, Arrays.asList(), Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall read(String emitter_address) { + final Function function = + new Function( + FUNC_READ, + Arrays.asList(new org.web3j.abi.datatypes.Address(160, emitter_address)), + Arrays.>asList(new TypeReference() {})); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall increment() { + final Function function = + new Function( + FUNC_INCREMENT, Arrays.asList(), Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall incrementRemote(String crossAddress) { + final Function function = + new Function( + FUNC_INCREMENTREMOTE, + Arrays.asList(new org.web3j.abi.datatypes.Address(160, crossAddress)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public List getNewEventEmitterEvents( + TransactionReceipt transactionReceipt) { + List valueList = + extractEventParametersWithLog(NEWEVENTEMITTER_EVENT, transactionReceipt); + ArrayList responses = + new ArrayList(valueList.size()); + for (Contract.EventValuesWithLog eventValues : valueList) { + NewEventEmitterEventResponse typedResponse = new NewEventEmitterEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.contractAddress = (String) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public Flowable newEventEmitterEventFlowable(EthFilter filter) { + return web3j + .ethLogFlowable(filter) + .map( + new io.reactivex.functions.Function() { + @Override + public NewEventEmitterEventResponse apply(Log log) { + Contract.EventValuesWithLog eventValues = + extractEventParametersWithLog(NEWEVENTEMITTER_EVENT, log); + NewEventEmitterEventResponse typedResponse = new NewEventEmitterEventResponse(); + typedResponse.log = log; + typedResponse.contractAddress = + (String) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + }); + } + + public Flowable newEventEmitterEventFlowable( + DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(NEWEVENTEMITTER_EVENT)); + return newEventEmitterEventFlowable(filter); + } + + @Deprecated + public static CrossContractReader load( + String contractAddress, + Web3j web3j, + Credentials credentials, + BigInteger gasPrice, + BigInteger gasLimit) { + return new CrossContractReader(contractAddress, web3j, credentials, gasPrice, gasLimit); + } + + @Deprecated + public static CrossContractReader load( + String contractAddress, + Web3j web3j, + TransactionManager transactionManager, + BigInteger gasPrice, + BigInteger gasLimit) { + return new CrossContractReader(contractAddress, web3j, transactionManager, gasPrice, gasLimit); + } + + public static CrossContractReader load( + String contractAddress, + Web3j web3j, + Credentials credentials, + ContractGasProvider contractGasProvider) { + return new CrossContractReader(contractAddress, web3j, credentials, contractGasProvider); + } + + public static CrossContractReader load( + String contractAddress, + Web3j web3j, + TransactionManager transactionManager, + ContractGasProvider contractGasProvider) { + return new CrossContractReader(contractAddress, web3j, transactionManager, contractGasProvider); + } + + public static RemoteCall deploy( + Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { + return deployRemoteCall( + CrossContractReader.class, web3j, credentials, contractGasProvider, BINARY, ""); + } + + @Deprecated + public static RemoteCall deploy( + Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { + return deployRemoteCall( + CrossContractReader.class, web3j, credentials, gasPrice, gasLimit, BINARY, ""); + } + + public static RemoteCall deploy( + Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { + return deployRemoteCall( + CrossContractReader.class, web3j, transactionManager, contractGasProvider, BINARY, ""); + } + + @Deprecated + public static RemoteCall deploy( + Web3j web3j, + TransactionManager transactionManager, + BigInteger gasPrice, + BigInteger gasLimit) { + return deployRemoteCall( + CrossContractReader.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, ""); + } + + public static class NewEventEmitterEventResponse extends BaseEventResponse { + public String contractAddress; + } +} diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/PrivateContractPublicStateAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/PrivateContractPublicStateAcceptanceTest.java new file mode 100644 index 0000000000..2672d24faf --- /dev/null +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/PrivateContractPublicStateAcceptanceTest.java @@ -0,0 +1,162 @@ +/* + * 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.web3j.privacy; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyAcceptanceTestBase; +import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyNode; +import org.hyperledger.besu.tests.web3j.generated.CrossContractReader; +import org.hyperledger.besu.tests.web3j.generated.EventEmitter; + +import java.math.BigInteger; + +import org.junit.Before; +import org.junit.Test; +import org.web3j.protocol.core.methods.response.TransactionReceipt; +import org.web3j.protocol.pantheon.response.privacy.PrivateTransactionReceipt; +import org.web3j.tx.exceptions.ContractCallException; + +public class PrivateContractPublicStateAcceptanceTest extends PrivacyAcceptanceTestBase { + private static final long POW_CHAIN_ID = 2018; + + private PrivacyNode minerNode; + + @Before + public void setUp() throws Exception { + minerNode = + privacyBesu.createPrivateTransactionEnabledMinerNode( + "miner-node", privacyAccountResolver.resolve(0)); + privacyCluster.start(minerNode); + } + + @Test + public void mustAllowAccessToPublicStateFromPrivateTx() throws Exception { + final EventEmitter publicEventEmitter = + minerNode.getBesu().execute((contractTransactions.createSmartContract(EventEmitter.class))); + + final TransactionReceipt receipt = publicEventEmitter.store(BigInteger.valueOf(12)).send(); + assertThat(receipt).isNotNull(); + + final CrossContractReader reader = + minerNode + .getBesu() + .execute( + privateContractTransactions.createSmartContract( + CrossContractReader.class, + minerNode.getTransactionSigningKey(), + POW_CHAIN_ID, + minerNode.getEnclaveKey())); + + assertThat(reader.read(publicEventEmitter.getContractAddress()).send()) + .isEqualTo(BigInteger.valueOf(12)); + } + + @Test(expected = ContractCallException.class) + public void mustNotAllowAccessToPrivateStateFromPublicTx() throws Exception { + final EventEmitter privateEventEmitter = + minerNode + .getBesu() + .execute( + (privateContractTransactions.createSmartContract( + EventEmitter.class, + minerNode.getTransactionSigningKey(), + POW_CHAIN_ID, + minerNode.getEnclaveKey()))); + + final TransactionReceipt receipt = privateEventEmitter.store(BigInteger.valueOf(12)).send(); + assertThat(receipt).isNotNull(); + + final CrossContractReader publicReader = + minerNode + .getBesu() + .execute(contractTransactions.createSmartContract(CrossContractReader.class)); + + publicReader.read(privateEventEmitter.getContractAddress()).send(); + } + + @Test + public void privateContractMustNotBeAbleToCallPublicContractWhichChangesState() throws Exception { + final CrossContractReader privateReader = + minerNode + .getBesu() + .execute( + privateContractTransactions.createSmartContract( + CrossContractReader.class, + minerNode.getTransactionSigningKey(), + POW_CHAIN_ID, + minerNode.getEnclaveKey())); + + final CrossContractReader publicReader = + minerNode + .getBesu() + .execute(contractTransactions.createSmartContract(CrossContractReader.class)); + + final PrivateTransactionReceipt transactionReceipt = + (PrivateTransactionReceipt) + privateReader.incrementRemote(publicReader.getContractAddress()).send(); + + assertThat(transactionReceipt.getOutput()).isEqualTo("0x"); + } + + @Test + public void privateContractMustNotBeAbleToCallPublicContractWhichInstantiatesContract() + throws Exception { + final CrossContractReader privateReader = + minerNode + .getBesu() + .execute( + privateContractTransactions.createSmartContract( + CrossContractReader.class, + minerNode.getTransactionSigningKey(), + POW_CHAIN_ID, + minerNode.getEnclaveKey())); + + final CrossContractReader publicReader = + minerNode + .getBesu() + .execute(contractTransactions.createSmartContract(CrossContractReader.class)); + + final PrivateTransactionReceipt transactionReceipt = + (PrivateTransactionReceipt) + privateReader.deployRemote(publicReader.getContractAddress()).send(); + + assertThat(transactionReceipt.getLogs().size()).isEqualTo(0); + } + + @Test + public void privateContractMustNotBeAbleToCallSelfDetructOfPublicContract() throws Exception { + final CrossContractReader privateReader = + minerNode + .getBesu() + .execute( + privateContractTransactions.createSmartContract( + CrossContractReader.class, + minerNode.getTransactionSigningKey(), + POW_CHAIN_ID, + minerNode.getEnclaveKey())); + + final CrossContractReader publicReader = + minerNode + .getBesu() + .execute(contractTransactions.createSmartContract(CrossContractReader.class)); + + final PrivateTransactionReceipt transactionReceipt = + (PrivateTransactionReceipt) + privateReader.remoteDestroy(publicReader.getContractAddress()).send(); + + assertThat(transactionReceipt.getOutput()).isEqualTo("0x"); + } +} diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java index 46c48fa193..aa2ff3820c 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java @@ -21,8 +21,8 @@ import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder; import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions; +import org.hyperledger.besu.ethereum.core.DefaultEvmAccount; import org.hyperledger.besu.ethereum.core.Hash; -import org.hyperledger.besu.ethereum.core.MutableAccount; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.core.SealableBlockHeader; @@ -294,9 +294,9 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator { } final Wei coinbaseReward = blockReward.plus(blockReward.times(ommers.size()).dividedBy(32)); final WorldUpdater updater = worldState.updater(); - final MutableAccount beneficiary = updater.getOrCreate(miningBeneficiary); + final DefaultEvmAccount beneficiary = updater.getOrCreate(miningBeneficiary); - beneficiary.incrementBalance(coinbaseReward); + beneficiary.getMutable().incrementBalance(coinbaseReward); for (final BlockHeader ommerHeader : ommers) { if (ommerHeader.getNumber() - header.getNumber() > MAX_GENERATION) { LOG.trace( @@ -307,10 +307,10 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator { return false; } - final MutableAccount ommerCoinbase = updater.getOrCreate(ommerHeader.getCoinbase()); + final DefaultEvmAccount ommerCoinbase = updater.getOrCreate(ommerHeader.getCoinbase()); final long distance = header.getNumber() - ommerHeader.getNumber(); final Wei ommerReward = blockReward.minus(blockReward.times(distance).dividedBy(8)); - ommerCoinbase.incrementBalance(ommerReward); + ommerCoinbase.getMutable().incrementBalance(ommerReward); } updater.commit(); diff --git a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/EntriesFromIntegrationTest.java b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/EntriesFromIntegrationTest.java index 81b2983253..bc7692106b 100644 --- a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/EntriesFromIntegrationTest.java +++ b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/EntriesFromIntegrationTest.java @@ -40,7 +40,7 @@ public class EntriesFromIntegrationTest { final MutableWorldState worldState = InMemoryStorageProvider.createInMemoryWorldStateArchive().getMutable(); final WorldUpdater updater = worldState.updater(); - MutableAccount account = updater.getOrCreate(Address.fromHexString("0x56")); + MutableAccount account = updater.getOrCreate(Address.fromHexString("0x56")).getMutable(); final Map expectedValues = new TreeMap<>(); final int nodeCount = 100_000; final Random random = new Random(42989428249L); @@ -53,7 +53,7 @@ public class EntriesFromIntegrationTest { updater.commit(); // Add some changes on top that AbstractWorldUpdater.UpdateTrackingAccount will have to merge. - account = worldState.updater().getOrCreate(Address.fromHexString("0x56")); + account = worldState.updater().getOrCreate(Address.fromHexString("0x56")).getMutable(); for (int i = 0; i <= nodeCount; i++) { addExpectedValue( account, expectedValues, UInt256.of(Math.abs(random.nextLong())), UInt256.of(i * 10 + 1)); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java index e3ee841663..01800216a9 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java @@ -112,7 +112,7 @@ public final class GenesisState { final WorldUpdater updater = target.updater(); genesisAccounts.forEach( genesisAccount -> { - final MutableAccount account = updater.getOrCreate(genesisAccount.address); + final MutableAccount account = updater.getOrCreate(genesisAccount.address).getMutable(); account.setNonce(genesisAccount.nonce); account.setBalance(genesisAccount.balance); account.setCode(genesisAccount.code); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/AbstractWorldUpdater.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/AbstractWorldUpdater.java index 54cbc99e02..35e5bd4644 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/AbstractWorldUpdater.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/AbstractWorldUpdater.java @@ -59,11 +59,12 @@ public abstract class AbstractWorldUpdater account = new UpdateTrackingAccount<>(address); account.setNonce(nonce); account.setBalance(balance); - return track(account); + return new DefaultEvmAccount(track(account)); } @Override @@ -80,11 +81,11 @@ public abstract class AbstractWorldUpdater(origin)); + return new DefaultEvmAccount(track(new UpdateTrackingAccount<>(origin))); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/DefaultEvmAccount.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/DefaultEvmAccount.java new file mode 100644 index 0000000000..c8d696ba70 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/DefaultEvmAccount.java @@ -0,0 +1,100 @@ +/* + * 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.core; + +import org.hyperledger.besu.util.bytes.Bytes32; +import org.hyperledger.besu.util.bytes.BytesValue; +import org.hyperledger.besu.util.uint.UInt256; + +import java.util.NavigableMap; + +public class DefaultEvmAccount implements EvmAccount { + private MutableAccount mutableAccount; + + public boolean isImmutable() { + return isImmutable; + } + + public void setImmutable(final boolean immutable) { + isImmutable = immutable; + } + + private boolean isImmutable; + + public DefaultEvmAccount(final MutableAccount mutableAccount) { + + this.mutableAccount = mutableAccount; + this.isImmutable = false; + } + + @Override + public MutableAccount getMutable() throws ModificationNotAllowedException { + if (isImmutable) { + throw new ModificationNotAllowedException(); + } + return mutableAccount; + } + + @Override + public Address getAddress() { + return mutableAccount.getAddress(); + } + + @Override + public Hash getAddressHash() { + return mutableAccount.getAddressHash(); + } + + @Override + public long getNonce() { + return mutableAccount.getNonce(); + } + + @Override + public Wei getBalance() { + return mutableAccount.getBalance(); + } + + @Override + public BytesValue getCode() { + return mutableAccount.getCode(); + } + + @Override + public Hash getCodeHash() { + return mutableAccount.getCodeHash(); + } + + @Override + public int getVersion() { + return mutableAccount.getVersion(); + } + + @Override + public UInt256 getStorageValue(final UInt256 key) { + return mutableAccount.getStorageValue(key); + } + + @Override + public UInt256 getOriginalStorageValue(final UInt256 key) { + return mutableAccount.getOriginalStorageValue(key); + } + + @Override + public NavigableMap storageEntriesFrom( + final Bytes32 startKeyHash, final int limit) { + return mutableAccount.storageEntriesFrom(startKeyHash, limit); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/EvmAccount.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/EvmAccount.java new file mode 100644 index 0000000000..26621f2988 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/EvmAccount.java @@ -0,0 +1,19 @@ +/* + * 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.core; + +public interface EvmAccount extends Account { + public MutableAccount getMutable() throws ModificationNotAllowedException; +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/ModificationNotAllowedException.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/ModificationNotAllowedException.java new file mode 100644 index 0000000000..9d74637996 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/ModificationNotAllowedException.java @@ -0,0 +1,21 @@ +/* + * 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.core; + +public class ModificationNotAllowedException extends RuntimeException { + ModificationNotAllowedException() { + super("This account may not be modified"); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/WorldUpdater.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/WorldUpdater.java index b4e52b3d42..84eda68aa0 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/WorldUpdater.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/WorldUpdater.java @@ -39,7 +39,7 @@ public interface WorldUpdater extends MutableWorldView { * @return the account {@code address}, which will have nonce {@code nonce}, balance {@code * balance} and empty code and storage. */ - MutableAccount createAccount(Address address, long nonce, Wei balance); + DefaultEvmAccount createAccount(Address address, long nonce, Wei balance); /** * Creates a new account, or reset it (that is, act as if it was deleted and created anew) if it @@ -52,7 +52,7 @@ public interface WorldUpdater extends MutableWorldView { * @return the account {@code address}, which will have 0 for the nonce and balance and empty code * and storage. */ - default MutableAccount createAccount(final Address address) { + default DefaultEvmAccount createAccount(final Address address) { return createAccount(address, Account.DEFAULT_NONCE, Account.DEFAULT_BALANCE); } @@ -61,11 +61,11 @@ public interface WorldUpdater extends MutableWorldView { * * @param address the address of the account. * @return the account {@code address}. If that account exists, it is returned as if by {@link - * #getMutable(Address)}, otherwise, it is created and returned as if by {@link + * #getAccount(Address)}, otherwise, it is created and returned as if by {@link * #createAccount(Address)} (and thus all his fields will be zero/empty). */ - default MutableAccount getOrCreate(final Address address) { - final MutableAccount account = getMutable(address); + default DefaultEvmAccount getOrCreate(final Address address) { + final DefaultEvmAccount account = getAccount(address); return account == null ? createAccount(address) : account; } @@ -74,10 +74,9 @@ public interface WorldUpdater extends MutableWorldView { * this updater). * * @param address the address of the account. - * @return the account {@code address} as modifiable object, or {@code null} if the account does - * not exist. + * @return the account {@code address}, or {@code null} if the account does not exist. */ - MutableAccount getMutable(Address address); + DefaultEvmAccount getAccount(Address address); /** * Deletes the provided account. diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessor.java index bd4706c10f..c2eac45c10 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessor.java @@ -167,7 +167,7 @@ public class MainnetBlockProcessor implements BlockProcessor { } final Wei coinbaseReward = blockReward.plus(blockReward.times(ommers.size()).dividedBy(32)); final WorldUpdater updater = worldState.updater(); - final MutableAccount coinbase = updater.getOrCreate(header.getCoinbase()); + final MutableAccount coinbase = updater.getOrCreate(header.getCoinbase()).getMutable(); coinbase.incrementBalance(coinbaseReward); for (final BlockHeader ommerHeader : ommers) { @@ -180,7 +180,8 @@ public class MainnetBlockProcessor implements BlockProcessor { return false; } - final MutableAccount ommerCoinbase = updater.getOrCreate(ommerHeader.getCoinbase()); + final MutableAccount ommerCoinbase = + updater.getOrCreate(ommerHeader.getCoinbase()).getMutable(); final long distance = header.getNumber() - ommerHeader.getNumber(); final Wei ommerReward = blockReward.minus(blockReward.times(distance).dividedBy(8)); ommerCoinbase.incrementBalance(ommerReward); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetContractCreationProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetContractCreationProcessor.java index 9d19b14c61..0edf89f0a9 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetContractCreationProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetContractCreationProcessor.java @@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.mainnet; import org.hyperledger.besu.ethereum.core.Account; import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Gas; +import org.hyperledger.besu.ethereum.core.ModificationNotAllowedException; import org.hyperledger.besu.ethereum.core.MutableAccount; import org.hyperledger.besu.ethereum.vm.EVM; import org.hyperledger.besu.ethereum.vm.GasCalculator; @@ -105,21 +106,27 @@ public class MainnetContractCreationProcessor extends AbstractMessageProcessor { if (LOG.isTraceEnabled()) { LOG.trace("Executing contract-creation"); } - - final MutableAccount sender = frame.getWorldState().getMutable(frame.getSenderAddress()); - sender.decrementBalance(frame.getValue()); - - final MutableAccount contract = frame.getWorldState().getOrCreate(frame.getContractAddress()); - if (accountExists(contract)) { - LOG.trace( - "Contract creation error: account as already been created for address {}", - frame.getContractAddress()); + try { + final MutableAccount sender = + frame.getWorldState().getAccount(frame.getSenderAddress()).getMutable(); + sender.decrementBalance(frame.getValue()); + + final MutableAccount contract = + frame.getWorldState().getOrCreate(frame.getContractAddress()).getMutable(); + if (accountExists(contract)) { + LOG.trace( + "Contract creation error: account as already been created for address {}", + frame.getContractAddress()); + frame.setState(MessageFrame.State.EXCEPTIONAL_HALT); + } else { + contract.incrementBalance(frame.getValue()); + contract.setNonce(initialContractNonce); + contract.clearStorage(); + frame.setState(MessageFrame.State.CODE_EXECUTING); + } + } catch (ModificationNotAllowedException ex) { + LOG.trace("Contract creation error: illegal modification not allowed from private state"); frame.setState(MessageFrame.State.EXCEPTIONAL_HALT); - } else { - contract.incrementBalance(frame.getValue()); - contract.setNonce(initialContractNonce); - contract.clearStorage(); - frame.setState(MessageFrame.State.CODE_EXECUTING); } } @@ -148,7 +155,7 @@ public class MainnetContractCreationProcessor extends AbstractMessageProcessor { // Finalize contract creation, setting the contract code. final MutableAccount contract = - frame.getWorldState().getOrCreate(frame.getContractAddress()); + frame.getWorldState().getOrCreate(frame.getContractAddress()).getMutable(); contract.setCode(contractCode); contract.setVersion(accountVersion); LOG.trace( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetMessageCallProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetMessageCallProcessor.java index 72c596ca76..3c794a7424 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetMessageCallProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetMessageCallProcessor.java @@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.mainnet; import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Gas; +import org.hyperledger.besu.ethereum.core.ModificationNotAllowedException; import org.hyperledger.besu.ethereum.core.MutableAccount; import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.vm.EVM; @@ -49,16 +50,20 @@ public class MainnetMessageCallProcessor extends AbstractMessageProcessor { @Override public void start(final MessageFrame frame) { LOG.trace("Executing message-call"); + try { + transferValue(frame); - transferValue(frame); - - // Check first if the message call is to a pre-compile contract - final PrecompiledContract precompile = - precompiles.get(frame.getContractAddress(), frame.getContractAccountVersion()); - if (precompile != null) { - executePrecompile(precompile, frame); - } else { - frame.setState(MessageFrame.State.CODE_EXECUTING); + // Check first if the message call is to a pre-compile contract + final PrecompiledContract precompile = + precompiles.get(frame.getContractAddress(), frame.getContractAccountVersion()); + if (precompile != null) { + executePrecompile(precompile, frame); + } else { + frame.setState(MessageFrame.State.CODE_EXECUTING); + } + } catch (ModificationNotAllowedException ex) { + LOG.trace("Message call error: illegal modification not allowed from private state"); + frame.setState(MessageFrame.State.EXCEPTIONAL_HALT); } } @@ -79,11 +84,12 @@ public class MainnetMessageCallProcessor extends AbstractMessageProcessor { * of the world state of this executor. */ private void transferValue(final MessageFrame frame) { - final MutableAccount senderAccount = frame.getWorldState().getMutable(frame.getSenderAddress()); + final MutableAccount senderAccount = + frame.getWorldState().getAccount(frame.getSenderAddress()).getMutable(); // The yellow paper explicitly states that if the recipient account doesn't exist at this // point, it is created. final MutableAccount recipientAccount = - frame.getWorldState().getOrCreate(frame.getRecipientAddress()); + frame.getWorldState().getOrCreate(frame.getRecipientAddress()).getMutable(); if (frame.getRecipientAddress().equals(frame.getSenderAddress())) { LOG.trace("Message call of {} to itself: no fund transferred", frame.getSenderAddress()); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index 96c7643776..6b2f0be129 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -370,9 +370,9 @@ public abstract class MainnetProtocolSpecs { .collect(Collectors.toList()); final WorldUpdater worldUpdater = worldState.updater(); final MutableAccount daoRefundContract = - worldUpdater.getOrCreate(DAO_REFUND_CONTRACT_ADDRESS); + worldUpdater.getOrCreate(DAO_REFUND_CONTRACT_ADDRESS).getMutable(); for (final Address address : addresses) { - final MutableAccount account = worldUpdater.getOrCreate(address); + final MutableAccount account = worldUpdater.getOrCreate(address).getMutable(); final Wei balance = account.getBalance(); account.decrementBalance(balance); daoRefundContract.incrementBalance(balance); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java index 36a0cbba9e..05989d3435 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java @@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.mainnet; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.Account; import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.DefaultEvmAccount; import org.hyperledger.besu.ethereum.core.Gas; import org.hyperledger.besu.ethereum.core.LogSeries; import org.hyperledger.besu.ethereum.core.MutableAccount; @@ -189,7 +190,7 @@ public class MainnetTransactionProcessor implements TransactionProcessor { } final Address senderAddress = transaction.getSender(); - final MutableAccount sender = worldState.getOrCreate(senderAddress); + final DefaultEvmAccount sender = worldState.getOrCreate(senderAddress); validationResult = transactionValidator.validateForSender(transaction, sender, transactionValidationParams); if (!validationResult.isValid()) { @@ -197,12 +198,13 @@ public class MainnetTransactionProcessor implements TransactionProcessor { return Result.invalid(validationResult); } - final long previousNonce = sender.incrementNonce(); + final MutableAccount senderMutableAccount = sender.getMutable(); + final long previousNonce = senderMutableAccount.incrementNonce(); LOG.trace( "Incremented sender {} nonce ({} -> {})", senderAddress, previousNonce, sender.getNonce()); final Wei upfrontGasCost = transaction.getUpfrontGasCost(); - final Wei previousBalance = sender.decrementBalance(upfrontGasCost); + final Wei previousBalance = senderMutableAccount.decrementBalance(upfrontGasCost); LOG.trace( "Deducted sender {} upfront gas cost {} ({} -> {})", senderAddress, @@ -307,9 +309,9 @@ public class MainnetTransactionProcessor implements TransactionProcessor { final Gas refundGas = initialFrame.getGasRefund().plus(selfDestructRefund); final Gas refunded = refunded(transaction, initialFrame.getRemainingGas(), refundGas); final Wei refundedWei = refunded.priceFor(transaction.getGasPrice()); - sender.incrementBalance(refundedWei); + senderMutableAccount.incrementBalance(refundedWei); - final MutableAccount coinbase = worldState.getOrCreate(miningBeneficiary); + final MutableAccount coinbase = worldState.getOrCreate(miningBeneficiary).getMutable(); final Gas coinbaseFee = Gas.of(transaction.getGasLimit()).minus(refunded); final Wei coinbaseWei = coinbaseFee.priceFor(transaction.getGasPrice()); coinbase.incrementBalance(coinbaseWei); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java index 1a37d47fc5..4d26de9f51 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java @@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.privacy; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.Account; import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.DefaultEvmAccount; import org.hyperledger.besu.ethereum.core.Gas; import org.hyperledger.besu.ethereum.core.LogSeries; import org.hyperledger.besu.ethereum.core.MutableAccount; @@ -33,6 +34,7 @@ import org.hyperledger.besu.ethereum.vm.Code; import org.hyperledger.besu.ethereum.vm.GasCalculator; import org.hyperledger.besu.ethereum.vm.MessageFrame; import org.hyperledger.besu.ethereum.vm.OperationTracer; +import org.hyperledger.besu.ethereum.worldstate.DefaultMutablePrivateWorldStateUpdater; import org.hyperledger.besu.util.bytes.BytesValue; import java.util.ArrayDeque; @@ -190,11 +192,11 @@ public class PrivateTransactionProcessor { LOG.trace("Starting private execution of {}", transaction); final Address senderAddress = transaction.getSender(); - final MutableAccount maybePrivateSender = privateWorldState.getMutable(senderAddress); + final DefaultEvmAccount maybePrivateSender = privateWorldState.getAccount(senderAddress); final MutableAccount sender = maybePrivateSender != null - ? maybePrivateSender - : privateWorldState.createAccount(senderAddress, 0, Wei.ZERO); + ? maybePrivateSender.getMutable() + : privateWorldState.createAccount(senderAddress, 0, Wei.ZERO).getMutable(); final ValidationResult validationResult = privateTransactionValidator.validate(transaction, sender.getNonce()); @@ -211,6 +213,10 @@ public class PrivateTransactionProcessor { final MessageFrame initialFrame; final Deque messageFrameStack = new ArrayDeque<>(); + + final WorldUpdater mutablePrivateWorldStateUpdater = + new DefaultMutablePrivateWorldStateUpdater(publicWorldState, privateWorldState); + if (transaction.isContractCreation()) { final Address privateContractAddress = Address.privateContractAddress(senderAddress, previousNonce, privacyGroupId); @@ -227,7 +233,7 @@ public class PrivateTransactionProcessor { .type(MessageFrame.Type.CONTRACT_CREATION) .messageFrameStack(messageFrameStack) .blockchain(blockchain) - .worldState(privateWorldState.updater()) + .worldState(mutablePrivateWorldStateUpdater) .address(privateContractAddress) .originator(senderAddress) .contract(privateContractAddress) @@ -256,7 +262,7 @@ public class PrivateTransactionProcessor { .type(MessageFrame.Type.MESSAGE_CALL) .messageFrameStack(messageFrameStack) .blockchain(blockchain) - .worldState(privateWorldState.updater()) + .worldState(mutablePrivateWorldStateUpdater) .address(to) .originator(senderAddress) .contract(to) @@ -285,7 +291,7 @@ public class PrivateTransactionProcessor { } if (initialFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { - privateWorldState.commit(); + mutablePrivateWorldStateUpdater.commit(); } if (initialFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java index 7c411cb7a8..31ddad3cd1 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java @@ -18,6 +18,7 @@ import static org.hyperledger.besu.util.uint.UInt256.U_32; import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Gas; +import org.hyperledger.besu.ethereum.core.ModificationNotAllowedException; import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.debug.TraceFrame; import org.hyperledger.besu.ethereum.debug.TraceOptions; @@ -82,10 +83,18 @@ public class DebugOperationTracer implements OperationTracer { if (!options.isStorageEnabled()) { return Optional.empty(); } - final Map storageContents = - new TreeMap<>( - frame.getWorldState().getMutable(frame.getRecipientAddress()).getUpdatedStorage()); - return Optional.of(storageContents); + try { + final Map storageContents = + new TreeMap<>( + frame + .getWorldState() + .getAccount(frame.getRecipientAddress()) + .getMutable() + .getUpdatedStorage()); + return Optional.of(storageContents); + } catch (ModificationNotAllowedException e) { + return Optional.of(new TreeMap<>()); + } } private Optional captureMemory(final MessageFrame frame) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/AbstractCreateOperation.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/AbstractCreateOperation.java index 9cbfdb64a7..ca116165b1 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/AbstractCreateOperation.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/AbstractCreateOperation.java @@ -57,7 +57,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation { final Wei value = Wei.wrap(frame.getStackItem(0)); final Address address = frame.getRecipientAddress(); - final MutableAccount account = frame.getWorldState().getMutable(address); + final MutableAccount account = frame.getWorldState().getAccount(address).getMutable(); frame.clearReturnData(); @@ -90,7 +90,8 @@ public abstract class AbstractCreateOperation extends AbstractOperation { private void spawnChildMessage(final MessageFrame frame) { final Address address = frame.getRecipientAddress(); - final MutableAccount account = frame.getWorldState().getMutable(address); + final MutableAccount account = frame.getWorldState().getAccount(address).getMutable(); + account.incrementNonce(); final Wei value = Wei.wrap(frame.getStackItem(0)); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/SStoreOperation.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/SStoreOperation.java index 93cbf80495..ba8ff3b629 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/SStoreOperation.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/SStoreOperation.java @@ -53,7 +53,8 @@ public class SStoreOperation extends AbstractOperation { final UInt256 key = frame.popStackItem().asUInt256(); final UInt256 value = frame.popStackItem().asUInt256(); - final MutableAccount account = frame.getWorldState().getMutable(frame.getRecipientAddress()); + final MutableAccount account = + frame.getWorldState().getAccount(frame.getRecipientAddress()).getMutable(); assert account != null : "VM account should exists"; // Increment the refund counter. diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/SelfDestructOperation.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/SelfDestructOperation.java index bd64f99956..54e00615ce 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/SelfDestructOperation.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/SelfDestructOperation.java @@ -48,12 +48,12 @@ public class SelfDestructOperation extends AbstractOperation { @Override public void execute(final MessageFrame frame) { final Address address = frame.getRecipientAddress(); - final MutableAccount account = frame.getWorldState().getMutable(address); + final MutableAccount account = frame.getWorldState().getAccount(address).getMutable(); frame.addSelfDestruct(address); final MutableAccount recipient = - frame.getWorldState().getOrCreate(Words.toAddress(frame.popStackItem())); + frame.getWorldState().getOrCreate(Words.toAddress(frame.popStackItem())).getMutable(); recipient.incrementBalance(account.getBalance()); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java new file mode 100644 index 0000000000..4a0152e109 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java @@ -0,0 +1,99 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.worldstate; + +import org.hyperledger.besu.ethereum.core.Account; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.DefaultEvmAccount; +import org.hyperledger.besu.ethereum.core.Wei; +import org.hyperledger.besu.ethereum.core.WorldUpdater; + +import java.util.Collection; + +public class DefaultMutablePrivateWorldStateUpdater implements WorldUpdater { + + private final WorldUpdater publicWorldUpdater; + private final WorldUpdater privateWorldUpdater; + + public DefaultMutablePrivateWorldStateUpdater( + final WorldUpdater publicWorldUpdater, final WorldUpdater privateWorldUpdater) { + this.publicWorldUpdater = publicWorldUpdater; + this.privateWorldUpdater = privateWorldUpdater; + } + + @Override + public DefaultEvmAccount createAccount( + final Address address, final long nonce, final Wei balance) { + return privateWorldUpdater.createAccount(address); + } + + @Override + public DefaultEvmAccount createAccount(final Address address) { + return privateWorldUpdater.createAccount(address); + } + + @Override + public DefaultEvmAccount getOrCreate(final Address address) { + return privateWorldUpdater.getOrCreate(address); + } + + @Override + public DefaultEvmAccount getAccount(final Address address) { + final DefaultEvmAccount privateAccount = privateWorldUpdater.getAccount(address); + if (privateAccount != null && !privateAccount.isEmpty()) { + return privateAccount; + } + final DefaultEvmAccount publicAccount = publicWorldUpdater.getAccount(address); + if (publicAccount != null && !publicAccount.isEmpty()) { + publicAccount.setImmutable(true); + return publicAccount; + } + return null; + } + + @Override + public void deleteAccount(final Address address) { + privateWorldUpdater.deleteAccount(address); + } + + @Override + public Collection getTouchedAccounts() { + return privateWorldUpdater.getTouchedAccounts(); + } + + @Override + public void revert() { + privateWorldUpdater.revert(); + } + + @Override + public void commit() { + privateWorldUpdater.commit(); + } + + @Override + public Account get(final Address address) { + final Account privateAccount = privateWorldUpdater.get(address); + if (privateAccount != null && !privateAccount.isEmpty()) { + return privateAccount; + } + return publicWorldUpdater.get(address); + } + + @Override + public WorldUpdater updater() { + return this; + } +} diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java index 3a1665ff7c..020aafa509 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java @@ -111,7 +111,7 @@ public class BlockDataGenerator { // Mutate accounts accountsToSetup.forEach( hash -> { - final MutableAccount a = stateUpdater.getMutable(hash); + final MutableAccount a = stateUpdater.getAccount(hash).getMutable(); a.incrementNonce(); a.setBalance(Wei.of(positiveLong())); storageKeys.forEach(key -> a.setStorageValue(key, UInt256.ONE)); @@ -150,7 +150,7 @@ public class BlockDataGenerator { WorldUpdater updater = worldState.updater(); List accounts = new ArrayList<>(count); for (int i = 0; i < count; i++) { - MutableAccount account = updater.getOrCreate(address()); + MutableAccount account = updater.getOrCreate(address()).getMutable(); if (random.nextFloat() < percentContractAccounts) { // Some percentage of accounts are contract accounts account.setCode(bytesValue(5, 50)); diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TestCodeExecutor.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TestCodeExecutor.java index ccf46fe5e8..0cff6b3a4d 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TestCodeExecutor.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TestCodeExecutor.java @@ -95,7 +95,8 @@ public class TestCodeExecutor { final MutableWorldState initialWorldState = stateArchive.getMutable(); final WorldUpdater worldState = initialWorldState.updater(); - final MutableAccount senderAccount = worldState.getOrCreate(TestCodeExecutor.SENDER_ADDRESS); + final MutableAccount senderAccount = + worldState.getOrCreate(TestCodeExecutor.SENDER_ADDRESS).getMutable(); accountSetup.accept(senderAccount); worldState.commit(); initialWorldState.persist(); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracerTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracerTest.java index ea228db66a..001dedc09f 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracerTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracerTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; +import org.hyperledger.besu.ethereum.core.DefaultEvmAccount; import org.hyperledger.besu.ethereum.core.Gas; import org.hyperledger.besu.ethereum.core.MessageFrameTestFixture; import org.hyperledger.besu.ethereum.core.MutableAccount; @@ -212,13 +213,15 @@ public class DebugOperationTracerTest { } private Map setupStorageForCapture(final MessageFrame frame) { - final MutableAccount account = mock(MutableAccount.class); - when(worldUpdater.getMutable(frame.getRecipientAddress())).thenReturn(account); + final DefaultEvmAccount account = mock(DefaultEvmAccount.class); + final MutableAccount mutableAccount = mock(MutableAccount.class); + when(account.getMutable()).thenReturn(mutableAccount); + when(worldUpdater.getAccount(frame.getRecipientAddress())).thenReturn(account); final Map updatedStorage = new TreeMap<>(); updatedStorage.put(UInt256.ZERO, UInt256.of(233)); updatedStorage.put(UInt256.ONE, UInt256.of(2424)); - when(account.getUpdatedStorage()).thenReturn(updatedStorage); + when(mutableAccount.getUpdatedStorage()).thenReturn(updatedStorage); final Bytes32 word1 = Bytes32.fromHexString("0x01"); final Bytes32 word2 = Bytes32.fromHexString("0x02"); final Bytes32 word3 = Bytes32.fromHexString("0x03"); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/WorldStateMock.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/WorldStateMock.java index 140170b936..a357349dd2 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/WorldStateMock.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/WorldStateMock.java @@ -89,7 +89,7 @@ public class WorldStateMock extends DefaultMutableWorldState { public static void insertAccount( final WorldUpdater updater, final Address address, final AccountMock toCopy) { - final MutableAccount account = updater.getOrCreate(address); + final MutableAccount account = updater.getOrCreate(address).getMutable(); account.setNonce(toCopy.getNonce()); account.setBalance(toCopy.getBalance()); account.setCode(toCopy.getCode()); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/ExtCodeHashOperationTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/ExtCodeHashOperationTest.java index 36cd3944f6..8f0080d10d 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/ExtCodeHashOperationTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/ExtCodeHashOperationTest.java @@ -74,7 +74,7 @@ public class ExtCodeHashOperationTest { @Test public void shouldReturnHashOfEmptyDataWhenAccountExistsButDoesNotHaveCode() { - worldStateUpdater.getOrCreate(REQUESTED_ADDRESS).setBalance(Wei.of(1)); + worldStateUpdater.getOrCreate(REQUESTED_ADDRESS).getMutable().setBalance(Wei.of(1)); assertThat(executeOperation(REQUESTED_ADDRESS)).isEqualTo(Hash.EMPTY); } @@ -92,14 +92,14 @@ public class ExtCodeHashOperationTest { @Test public void shouldReturnEmptyCodeHashWhenPrecompileHasBalance() { // Sending money to a precompile causes it to exist in the world state archive. - worldStateUpdater.getOrCreate(Address.ECREC).setBalance(Wei.of(10)); + worldStateUpdater.getOrCreate(Address.ECREC).getMutable().setBalance(Wei.of(10)); assertThat(executeOperation(Address.ECREC)).isEqualTo(Hash.EMPTY); } @Test public void shouldGetHashOfAccountCodeWhenCodeIsPresent() { final BytesValue code = BytesValue.fromHexString("0xabcdef"); - final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS); + final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS).getMutable(); account.setCode(code); account.setVersion(Account.DEFAULT_VERSION); assertThat(executeOperation(REQUESTED_ADDRESS)).isEqualTo(Hash.hash(code)); @@ -109,7 +109,7 @@ public class ExtCodeHashOperationTest { public void shouldZeroOutLeftMostBitsToGetAddress() { // If EXTCODEHASH of A is X, then EXTCODEHASH of A + 2**160 is X. final BytesValue code = BytesValue.fromHexString("0xabcdef"); - final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS); + final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS).getMutable(); account.setCode(code); account.setVersion(Account.DEFAULT_VERSION); final Bytes32 value = diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/SStoreOperationTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/SStoreOperationTest.java index 0f2f8055d8..1a0eaf5f11 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/SStoreOperationTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/SStoreOperationTest.java @@ -103,7 +103,7 @@ public class SStoreOperationTest { .blockchain(blockchain) .initialGas(initialGas) .build(); - worldStateUpdater.getOrCreate(address).setBalance(Wei.of(1)); + worldStateUpdater.getOrCreate(address).getMutable().setBalance(Wei.of(1)); worldStateUpdater.commit(); frame.setGasRemaining(remainingGas); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldStateTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldStateTest.java index c7a6ef2954..2b2ef18774 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldStateTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldStateTest.java @@ -83,7 +83,7 @@ public class DefaultMutableWorldStateTest { public void containsAccount_AccountExists() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - updater.createAccount(ADDRESS).setBalance(Wei.of(100000)); + updater.createAccount(ADDRESS).getMutable().setBalance(Wei.of(100000)); updater.commit(); assertThat(worldState.get(ADDRESS)).isNotNull(); assertThat(worldState.rootHash()) @@ -108,7 +108,7 @@ public class DefaultMutableWorldStateTest { public void removeAccount_UpdatedAccount() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - updater.createAccount(ADDRESS).setBalance(Wei.of(100000)); + updater.createAccount(ADDRESS).getMutable().setBalance(Wei.of(100000)); updater.deleteAccount(ADDRESS); updater.commit(); assertThat(worldState.rootHash()).isEqualTo(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH); @@ -122,7 +122,7 @@ public class DefaultMutableWorldStateTest { // Create a world state with one account final MutableWorldState worldState = createEmpty(); WorldUpdater updater = worldState.updater(); - updater.createAccount(ADDRESS).setBalance(Wei.of(100000)); + updater.createAccount(ADDRESS).getMutable().setBalance(Wei.of(100000)); updater.commit(); assertThat(worldState.get(ADDRESS)).isNotNull(); assertThat(worldState.rootHash()).isNotEqualTo(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH); @@ -131,7 +131,7 @@ public class DefaultMutableWorldStateTest { updater = worldState.updater(); updater.deleteAccount(ADDRESS); assertThat(updater.get(ADDRESS)).isNull(); - assertThat(updater.getMutable(ADDRESS)).isNull(); + assertThat(updater.getAccount(ADDRESS)).isNull(); updater.commit(); assertThat(updater.get(ADDRESS)).isNull(); @@ -143,7 +143,7 @@ public class DefaultMutableWorldStateTest { // Create a world state with one account final MutableWorldState worldState = createEmpty(); WorldUpdater updater = worldState.updater(); - updater.createAccount(ADDRESS).setBalance(Wei.of(100000)); + updater.createAccount(ADDRESS).getMutable().setBalance(Wei.of(100000)); updater.commit(); worldState.persist(); assertThat(worldState.get(ADDRESS)).isNotNull(); @@ -153,7 +153,7 @@ public class DefaultMutableWorldStateTest { updater = worldState.updater(); updater.deleteAccount(ADDRESS); assertThat(updater.get(ADDRESS)).isNull(); - assertThat(updater.getMutable(ADDRESS)).isNull(); + assertThat(updater.getAccount(ADDRESS)).isNull(); // Check account is gone after committing updater.commit(); assertThat(updater.get(ADDRESS)).isNull(); @@ -175,7 +175,7 @@ public class DefaultMutableWorldStateTest { public void streamAccounts_singleAccount() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - updater.createAccount(ADDRESS).setBalance(Wei.of(100000)); + updater.createAccount(ADDRESS).getMutable().setBalance(Wei.of(100000)); updater.commit(); List accounts = @@ -201,10 +201,10 @@ public class DefaultMutableWorldStateTest { final WorldUpdater updater = worldState.updater(); // Create an account - final MutableAccount accountA = updater.createAccount(addr1); + final MutableAccount accountA = updater.createAccount(addr1).getMutable(); accountA.setBalance(Wei.of(100000)); // Create another - final MutableAccount accountB = updater.createAccount(addr2); + final MutableAccount accountB = updater.createAccount(addr2).getMutable(); accountB.setNonce(1); // Commit changes updater.commit(); @@ -249,7 +249,7 @@ public class DefaultMutableWorldStateTest { Hash.fromHexString("0xa3e1c133a5a51b03399ed9ad0380f3182e9e18322f232b816dd4b9094f871e1b"); // Update account and assert we get the expected response from updater - updater.createAccount(ADDRESS).setBalance(newBalance); + updater.createAccount(ADDRESS).getMutable().setBalance(newBalance); assertThat(updater.get(ADDRESS)).isNotNull(); assertThat(updater.get(ADDRESS).getBalance()).isEqualTo(newBalance); @@ -285,7 +285,7 @@ public class DefaultMutableWorldStateTest { public void getAccountNonce_AccountExists() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - updater.createAccount(ADDRESS).setNonce(1L); + updater.createAccount(ADDRESS).getMutable().setNonce(1L); updater.commit(); assertThat(worldState.get(ADDRESS).getNonce()).isEqualTo(1L); assertThat(worldState.rootHash()) @@ -298,7 +298,7 @@ public class DefaultMutableWorldStateTest { public void replaceAccountNonce() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.createAccount(ADDRESS); + final MutableAccount account = updater.createAccount(ADDRESS).getMutable(); account.setNonce(1L); account.setNonce(2L); updater.commit(); @@ -313,7 +313,7 @@ public class DefaultMutableWorldStateTest { public void getAccountBalance_AccountExists() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - updater.createAccount(ADDRESS).setBalance(Wei.of(100000)); + updater.createAccount(ADDRESS).getMutable().setBalance(Wei.of(100000)); updater.commit(); assertThat(worldState.get(ADDRESS).getBalance()).isEqualTo(Wei.of(100000)); } @@ -322,7 +322,7 @@ public class DefaultMutableWorldStateTest { public void replaceAccountBalance() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.createAccount(ADDRESS); + final MutableAccount account = updater.createAccount(ADDRESS).getMutable(); account.setBalance(Wei.of(100000)); account.setBalance(Wei.of(200000)); updater.commit(); @@ -337,7 +337,7 @@ public class DefaultMutableWorldStateTest { public void setStorageValue_ZeroValue() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.createAccount(ADDRESS); + final MutableAccount account = updater.createAccount(ADDRESS).getMutable(); account.setBalance(Wei.of(100000)); account.setStorageValue(UInt256.ZERO, UInt256.ZERO); updater.commit(); @@ -352,7 +352,7 @@ public class DefaultMutableWorldStateTest { public void setStorageValue_NonzeroValue() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.createAccount(ADDRESS); + final MutableAccount account = updater.createAccount(ADDRESS).getMutable(); account.setBalance(Wei.of(100000)); account.setStorageValue(UInt256.ONE, UInt256.of(2)); updater.commit(); @@ -367,7 +367,7 @@ public class DefaultMutableWorldStateTest { public void replaceStorageValue_NonzeroValue() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.createAccount(ADDRESS); + final MutableAccount account = updater.createAccount(ADDRESS).getMutable(); account.setBalance(Wei.of(100000)); account.setStorageValue(UInt256.ONE, UInt256.of(2)); account.setStorageValue(UInt256.ONE, UInt256.of(3)); @@ -383,7 +383,7 @@ public class DefaultMutableWorldStateTest { public void replaceStorageValue_ZeroValue() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.createAccount(ADDRESS); + final MutableAccount account = updater.createAccount(ADDRESS).getMutable(); account.setBalance(Wei.of(100000)); account.setStorageValue(UInt256.ONE, UInt256.of(2)); account.setStorageValue(UInt256.ONE, UInt256.ZERO); @@ -398,12 +398,12 @@ public class DefaultMutableWorldStateTest { public void getOriginalStorageValue() { final MutableWorldState worldState = createEmpty(); final WorldUpdater setupUpdater = worldState.updater(); - final MutableAccount setupAccount = setupUpdater.createAccount(ADDRESS); + final MutableAccount setupAccount = setupUpdater.createAccount(ADDRESS).getMutable(); setupAccount.setStorageValue(UInt256.ONE, UInt256.of(2)); setupUpdater.commit(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.getOrCreate(ADDRESS); + final MutableAccount account = updater.getOrCreate(ADDRESS).getMutable(); assertThat(account.getOriginalStorageValue(UInt256.ONE)).isEqualTo(UInt256.of(2)); account.setStorageValue(UInt256.ONE, UInt256.of(3)); @@ -414,12 +414,12 @@ public class DefaultMutableWorldStateTest { public void originalStorageValueIsAlwaysZeroIfStorageWasCleared() { final MutableWorldState worldState = createEmpty(); final WorldUpdater setupUpdater = worldState.updater(); - final MutableAccount setupAccount = setupUpdater.createAccount(ADDRESS); + final MutableAccount setupAccount = setupUpdater.createAccount(ADDRESS).getMutable(); setupAccount.setStorageValue(UInt256.ONE, UInt256.of(2)); setupUpdater.commit(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.getOrCreate(ADDRESS); + final MutableAccount account = updater.getOrCreate(ADDRESS).getMutable(); account.clearStorage(); assertThat(account.getOriginalStorageValue(UInt256.ONE)).isEqualTo(UInt256.ZERO); @@ -433,13 +433,13 @@ public class DefaultMutableWorldStateTest { // Create a world state with one account final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - MutableAccount account = updater.createAccount(ADDRESS); + MutableAccount account = updater.createAccount(ADDRESS).getMutable(); account.setBalance(Wei.of(100000)); account.setStorageValue(storageKey, storageValue); assertThat(account.getStorageValue(storageKey)).isEqualTo(storageValue); // Clear storage - account = updater.getMutable(ADDRESS); + account = updater.getAccount(ADDRESS).getMutable(); assertThat(account).isNotNull(); assertThat(account.getStorageValue(storageKey)).isEqualTo(storageValue); account.clearStorage(); @@ -448,12 +448,12 @@ public class DefaultMutableWorldStateTest { // Check storage is cleared after committing updater.commit(); - assertThat(updater.getMutable(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); + assertThat(updater.getAccount(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); assertThat(updater.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); assertThat(worldState.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); // And after persisting - assertThat(updater.getMutable(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); + assertThat(updater.getAccount(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); assertThat(updater.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); assertThat(worldState.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); } @@ -466,7 +466,7 @@ public class DefaultMutableWorldStateTest { // Create a world state with one account final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - MutableAccount account = updater.createAccount(ADDRESS); + MutableAccount account = updater.createAccount(ADDRESS).getMutable(); account.setBalance(Wei.of(100000)); account.setStorageValue(storageKey, storageValue); updater.commit(); @@ -475,7 +475,7 @@ public class DefaultMutableWorldStateTest { assertThat(worldState.rootHash()).isNotEqualTo(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH); // Clear storage - account = updater.getMutable(ADDRESS); + account = updater.getAccount(ADDRESS).getMutable(); assertThat(account).isNotNull(); assertThat(account.getStorageValue(storageKey)).isEqualTo(storageValue); account.clearStorage(); @@ -485,12 +485,12 @@ public class DefaultMutableWorldStateTest { // Check storage is cleared after committing updater.commit(); - assertThat(updater.getMutable(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); + assertThat(updater.getAccount(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); assertThat(updater.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); assertThat(worldState.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); // And after persisting - assertThat(updater.getMutable(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); + assertThat(updater.getAccount(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); assertThat(updater.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); assertThat(worldState.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(UInt256.ZERO); } @@ -504,13 +504,13 @@ public class DefaultMutableWorldStateTest { // Create a world state with one account final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - MutableAccount account = updater.createAccount(ADDRESS); + MutableAccount account = updater.createAccount(ADDRESS).getMutable(); account.setBalance(Wei.of(100000)); account.setStorageValue(storageKey, originalStorageValue); assertThat(account.getStorageValue(storageKey)).isEqualTo(originalStorageValue); // Clear storage then edit - account = updater.getMutable(ADDRESS); + account = updater.getAccount(ADDRESS).getMutable(); assertThat(account).isNotNull(); assertThat(account.getStorageValue(storageKey)).isEqualTo(originalStorageValue); assertThat(updater.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(originalStorageValue); @@ -520,12 +520,12 @@ public class DefaultMutableWorldStateTest { // Check storage is cleared after committing updater.commit(); - assertThat(updater.getMutable(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); + assertThat(updater.getAccount(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); assertThat(updater.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); assertThat(worldState.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); // And after persisting - assertThat(updater.getMutable(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); + assertThat(updater.getAccount(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); assertThat(updater.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); assertThat(worldState.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); } @@ -539,7 +539,7 @@ public class DefaultMutableWorldStateTest { // Create a world state with one account final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - MutableAccount account = updater.createAccount(ADDRESS); + MutableAccount account = updater.createAccount(ADDRESS).getMutable(); account.setBalance(Wei.of(100000)); account.setStorageValue(storageKey, originalStorageValue); assertThat(account.getStorageValue(storageKey)).isEqualTo(originalStorageValue); @@ -547,7 +547,7 @@ public class DefaultMutableWorldStateTest { worldState.persist(); // Clear storage then edit - account = updater.getMutable(ADDRESS); + account = updater.getAccount(ADDRESS).getMutable(); assertThat(account).isNotNull(); assertThat(account.getStorageValue(storageKey)).isEqualTo(originalStorageValue); assertThat(updater.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(originalStorageValue); @@ -558,12 +558,12 @@ public class DefaultMutableWorldStateTest { // Check storage is cleared after committing updater.commit(); - assertThat(updater.getMutable(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); + assertThat(updater.getAccount(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); assertThat(updater.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); assertThat(worldState.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); // And after persisting - assertThat(updater.getMutable(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); + assertThat(updater.getAccount(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); assertThat(updater.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); assertThat(worldState.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(newStorageValue); } @@ -572,7 +572,7 @@ public class DefaultMutableWorldStateTest { public void replaceAccountCode() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.createAccount(ADDRESS); + final MutableAccount account = updater.createAccount(ADDRESS).getMutable(); account.setBalance(Wei.of(100000)); account.setCode(BytesValue.of(1, 2, 3)); account.setVersion(Account.DEFAULT_VERSION); @@ -590,12 +590,12 @@ public class DefaultMutableWorldStateTest { public void revert() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater1 = worldState.updater(); - final MutableAccount account1 = updater1.createAccount(ADDRESS); + final MutableAccount account1 = updater1.createAccount(ADDRESS).getMutable(); account1.setBalance(Wei.of(200000)); updater1.commit(); final WorldUpdater updater2 = worldState.updater(); - final MutableAccount account2 = updater2.getMutable(ADDRESS); + final MutableAccount account2 = updater2.getAccount(ADDRESS).getMutable(); account2.setBalance(Wei.of(300000)); assertThat(updater2.get(ADDRESS).getBalance()).isEqualTo(Wei.of(300000)); @@ -615,7 +615,7 @@ public class DefaultMutableWorldStateTest { public void shouldReturnNullForGetMutableWhenAccountDeletedInAncestor() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater1 = worldState.updater(); - final MutableAccount account1 = updater1.createAccount(ADDRESS); + final MutableAccount account1 = updater1.createAccount(ADDRESS).getMutable(); updater1.commit(); assertThat(updater1.get(ADDRESS)) .isEqualToComparingOnlyGivenFields(account1, "address", "nonce", "balance", "codeHash"); @@ -625,14 +625,14 @@ public class DefaultMutableWorldStateTest { assertThat(updater2.get(ADDRESS)).isEqualTo(null); final WorldUpdater updater3 = updater2.updater(); - assertThat(updater3.getMutable(ADDRESS)).isEqualTo(null); + assertThat(updater3.getAccount(ADDRESS)).isEqualTo(null); } @Test public void shouldCombineUnchangedAndChangedValuesWhenRetrievingStorageEntries() { final MutableWorldState worldState = createEmpty(); WorldUpdater updater = worldState.updater(); - MutableAccount account = updater.createAccount(ADDRESS); + MutableAccount account = updater.createAccount(ADDRESS).getMutable(); account.setBalance(Wei.of(100000)); account.setStorageValue(UInt256.ONE, UInt256.of(2)); account.setStorageValue(UInt256.of(2), UInt256.of(5)); @@ -645,7 +645,7 @@ public class DefaultMutableWorldStateTest { initialSetOfEntries.forEach(entry -> initialEntries.put(entry.getKeyHash(), entry)); updater = worldState.updater(); - account = updater.getMutable(ADDRESS); + account = updater.getAccount(ADDRESS).getMutable(); account.setStorageValue(UInt256.ONE, UInt256.of(3)); account.setStorageValue(UInt256.of(3), UInt256.of(6));