mirror of https://github.com/hyperledger/besu
Fix #1110: Privacy: Restart caused by insufficient memory can cause inconsistent private state (#1206)
* Fix #1110: Privacy: Restart caused by insufficient memory can cause inconsistent private state Signed-off-by: Stefan Pingel <stefan.pingel@consensys.net>pull/1354/head
parent
473ac77275
commit
eb3941976c
@ -0,0 +1,83 @@ |
||||
/* |
||||
* 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.storage; |
||||
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionReceipt; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes32; |
||||
|
||||
public class PrivateMetadataUpdater { |
||||
|
||||
private final PrivateStateStorage privateStateKeyValueStorage; |
||||
private final BlockHeader blockHeader; |
||||
private final PrivateStateStorage.Updater updater; |
||||
private final PrivacyGroupHeadBlockMap privacyGroupHeadBlockMap; |
||||
private final Map<Bytes32, PrivateBlockMetadata> privateBlockMetadataMap = new HashMap<>(); |
||||
|
||||
public PrivateMetadataUpdater( |
||||
final BlockHeader blockHeader, final PrivateStateStorage keyValueStorage) { |
||||
this.privateStateKeyValueStorage = keyValueStorage; |
||||
this.blockHeader = blockHeader; |
||||
this.updater = privateStateKeyValueStorage.updater(); |
||||
this.privacyGroupHeadBlockMap = |
||||
privateStateKeyValueStorage |
||||
.getPrivacyGroupHeadBlockMap(blockHeader.getParentHash()) |
||||
.orElse(PrivacyGroupHeadBlockMap.empty()); |
||||
} |
||||
|
||||
public PrivateBlockMetadata getPrivateBlockMetadata(final Bytes32 privacyGroupId) { |
||||
return privateBlockMetadataMap.get(privacyGroupId); |
||||
} |
||||
|
||||
public PrivacyGroupHeadBlockMap getPrivacyGroupHeadBlockMap() { |
||||
return privacyGroupHeadBlockMap; |
||||
} |
||||
|
||||
public void putTransactionReceipt( |
||||
final Bytes32 transactionHash, final PrivateTransactionReceipt receipt) { |
||||
updater.putTransactionReceipt(blockHeader.getBlockHash(), transactionHash, receipt); |
||||
} |
||||
|
||||
public void addPrivateTransactionMetadata( |
||||
final Bytes32 privacyGroupId, final PrivateTransactionMetadata metadata) { |
||||
PrivateBlockMetadata privateBlockMetadata = privateBlockMetadataMap.get(privacyGroupId); |
||||
if (privateBlockMetadata == null) { |
||||
privateBlockMetadata = PrivateBlockMetadata.empty(); |
||||
} |
||||
privateBlockMetadata.addPrivateTransactionMetadata(metadata); |
||||
privateBlockMetadataMap.put(privacyGroupId, privateBlockMetadata); |
||||
} |
||||
|
||||
public void updatePrivacyGroupHeadBlockMap(final Bytes32 privacyGroupId) { |
||||
privacyGroupHeadBlockMap.put(privacyGroupId, blockHeader.getHash()); |
||||
} |
||||
|
||||
public void commit() { |
||||
if (privacyGroupHeadBlockMap.size() > 0) { |
||||
updater.putPrivacyGroupHeadBlockMap(blockHeader.getHash(), privacyGroupHeadBlockMap); |
||||
} |
||||
privateBlockMetadataMap.entrySet().stream() |
||||
.forEach( |
||||
entry -> |
||||
updater.putPrivateBlockMetadata( |
||||
blockHeader.getHash(), entry.getKey(), entry.getValue())); |
||||
|
||||
updater.commit(); |
||||
} |
||||
} |
@ -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.ethereum.privacy; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||
import org.hyperledger.besu.ethereum.core.Hash; |
||||
import org.hyperledger.besu.ethereum.core.InMemoryPrivacyStorageProvider; |
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap; |
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateBlockMetadata; |
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater; |
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; |
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateTransactionMetadata; |
||||
|
||||
import java.util.Optional; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes32; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
public class PrivateMetadataUpdaterTest { |
||||
|
||||
private PrivateMetadataUpdater updater; |
||||
private BlockHeader blockHeader; |
||||
private PrivateStateStorage privateStateStorage; |
||||
private Hash hashBlockOne; |
||||
private Bytes32 privacyGroupId; |
||||
private Hash stateRoot; |
||||
private Hash pmtHash; |
||||
|
||||
@Before |
||||
public void before() { |
||||
blockHeader = mock(BlockHeader.class); |
||||
privateStateStorage = new InMemoryPrivacyStorageProvider().createPrivateStateStorage(); |
||||
final Hash hashBlockZero = Hash.ZERO; |
||||
when(blockHeader.getParentHash()).thenReturn(hashBlockZero); |
||||
updater = new PrivateMetadataUpdater(blockHeader, privateStateStorage); |
||||
hashBlockOne = |
||||
Hash.fromHexString("1111111111111111111111111111111111111111111111111111111111111111"); |
||||
stateRoot = |
||||
Hash.fromHexString("2222222222222222222222222222222222222222222222222222222222222222"); |
||||
privacyGroupId = |
||||
Bytes32.fromHexString("3333333333333333333333333333333333333333333333333333333333333333"); |
||||
} |
||||
|
||||
@Test |
||||
public void returnsEmptyPrivateGroupHeadBlockMapForUnknownBlock() { |
||||
assertThat(updater.getPrivacyGroupHeadBlockMap()).isEqualTo(PrivacyGroupHeadBlockMap.empty()); |
||||
} |
||||
|
||||
@Test |
||||
public void addingMetadataSuccessfull() { |
||||
when(blockHeader.getHash()).thenReturn(hashBlockOne); |
||||
pmtHash = Hash.ZERO; |
||||
final PrivateTransactionMetadata expected = new PrivateTransactionMetadata(pmtHash, stateRoot); |
||||
updater.addPrivateTransactionMetadata(privacyGroupId, expected); |
||||
updater.commit(); |
||||
final Optional<PrivateBlockMetadata> privateBlockMetadata = |
||||
privateStateStorage.getPrivateBlockMetadata(hashBlockOne, privacyGroupId); |
||||
assertThat(privateBlockMetadata.get().getLatestStateRoot().get()).isEqualTo(stateRoot); |
||||
} |
||||
|
||||
@Test |
||||
public void updatesPrivacyGroupHeadBlockMap() { |
||||
when(blockHeader.getHash()).thenReturn(hashBlockOne); |
||||
updater.updatePrivacyGroupHeadBlockMap(privacyGroupId); |
||||
updater.commit(); |
||||
final PrivacyGroupHeadBlockMap actual = |
||||
privateStateStorage.getPrivacyGroupHeadBlockMap(hashBlockOne).get(); |
||||
assertThat(actual.get(privacyGroupId)).isEqualTo(hashBlockOne); |
||||
} |
||||
} |
Loading…
Reference in new issue