Skip to content

Commit

Permalink
feat(state): add virtual map for contract key/values
Browse files Browse the repository at this point in the history
Signed-off-by: failfmi <oscurocalma@gmail.com>
  • Loading branch information
failfmi committed Oct 18, 2021
1 parent af597d3 commit f614637
Show file tree
Hide file tree
Showing 10 changed files with 416 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ hedera-node/data/onboard/feeSchedule.txt
hedera-node/data/onboard/GenesisPub32Key.txt
hedera-node/data/recordstreams/
hedera-node/data/saved/
hedera-node/data/jasperdb/
hedera-node/output/
hedera-node/target/
hedera-node/src/test/resources/diskFs
Expand Down
8 changes: 8 additions & 0 deletions hedera-node/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@
<groupId>com.swirlds</groupId>
<artifactId>swirlds-fchashmap</artifactId>
</dependency>
<dependency>
<groupId>com.swirlds</groupId>
<artifactId>swirlds-jasperdb</artifactId>
</dependency>
<dependency>
<groupId>com.swirlds</groupId>
<artifactId>swirlds-merkle</artifactId>
Expand All @@ -265,6 +269,10 @@
<groupId>com.swirlds</groupId>
<artifactId>swirlds-fcqueue</artifactId>
</dependency>
<dependency>
<groupId>com.swirlds</groupId>
<artifactId>swirlds-virtualmap</artifactId>
</dependency>
<dependency>
<groupId>com.hedera.hashgraph</groupId>
<artifactId>ethereumj-core</artifactId>
Expand Down
56 changes: 53 additions & 3 deletions hedera-node/src/main/java/com/hedera/services/ServicesState.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@

import com.google.protobuf.InvalidProtocolBufferException;
import com.hedera.services.context.properties.BootstrapProperties;
import com.hedera.services.contracts.virtual.ContractKey;
import com.hedera.services.contracts.virtual.ContractKeySerializer;
import com.hedera.services.contracts.virtual.ContractValue;
import com.hedera.services.state.merkle.MerkleAccount;
import com.hedera.services.state.merkle.MerkleBlobMeta;
import com.hedera.services.state.merkle.MerkleDiskFs;
Expand Down Expand Up @@ -59,11 +62,19 @@
import com.swirlds.common.merkle.utility.AbstractNaryMerkleInternal;
import com.swirlds.common.merkle.utility.Keyed;
import com.swirlds.fchashmap.FCOneToManyRelation;
import com.swirlds.jasperdb.VirtualDataSourceJasperDB;
import com.swirlds.jasperdb.VirtualInternalRecordSerializer;
import com.swirlds.jasperdb.VirtualLeafRecordSerializer;
import com.swirlds.merkle.map.FCMapMigration;
import com.swirlds.merkle.map.MerkleMap;
import com.swirlds.virtualmap.VirtualMap;
import com.swirlds.virtualmap.datasource.VirtualDataSource;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.time.Instant;
import java.util.List;
import java.util.concurrent.CompletableFuture;
Expand All @@ -74,8 +85,8 @@
import static com.hedera.services.context.AppsManager.APPS;
import static com.hedera.services.state.merkle.MerkleNetworkContext.UNKNOWN_CONSENSUS_TIME;
import static com.hedera.services.state.migration.Release0170Migration.moveLargeFcmsToBinaryRoutePositions;
import static com.hedera.services.utils.EntityNumPair.fromLongs;
import static com.hedera.services.utils.EntityIdUtils.parseAccount;
import static com.hedera.services.utils.EntityNumPair.fromLongs;
import static java.util.concurrent.CompletableFuture.runAsync;

/**
Expand Down Expand Up @@ -135,6 +146,8 @@ public int getMinimumChildCount(int version) {
return StateChildIndices.NUM_PRE_0160_CHILDREN;
} else if (version <= StateVersions.RELEASE_0180_VERSION) {
return StateChildIndices.NUM_POST_0160_CHILDREN;
} else if (version <= StateVersions.RELEASE_0200_VERSION) {
return StateChildIndices.NUM_POST_0200_CHILDREN;
} else {
throw new IllegalArgumentException("Argument 'version='" + version + "' is invalid!");
}
Expand Down Expand Up @@ -233,7 +246,7 @@ public void migrate() {
}

/* --- SwirldState --- */
@Override
// @Override
public void init(Platform platform, AddressBook addressBook) {
if (deserializedVersion < StateVersions.RELEASE_0180_VERSION && platform != platformForDeferredInit) {
/* Due to design issues with the BinaryObjectStore, which will not be finished
Expand All @@ -252,8 +265,12 @@ public void init(Platform platform, AddressBook addressBook) {
internalInit(platform, new BootstrapProperties());
}

public void init(Platform platform, AddressBook addressBook, SwirldDualState dualState) {
init(platform, addressBook);
}

@Override
public void genesisInit(Platform platform, AddressBook addressBook) {
public void genesisInit(Platform platform, AddressBook addressBook, SwirldDualState dualState) {
log.info("Init called on Services node {} WITHOUT Merkle saved state", platform.getSelfId());

/* Create the top-level children in the Merkle tree */
Expand Down Expand Up @@ -404,6 +421,10 @@ public FCOneToManyRelation<EntityNum, Long> uniqueTreasuryOwnershipAssociations(
return metadata.getUniqueTreasuryOwnershipAssociations();
}

public VirtualMap<ContractKey, ContractValue> contractStorage() {
return getChild(StateChildIndices.CONTRACT_STORAGE);
}

private void internalInit(Platform platform, BootstrapProperties bootstrapProps) {
networkCtx().setStateVersion(StateVersions.CURRENT_VERSION);
diskFs().checkHashesAgainstDiskContents();
Expand Down Expand Up @@ -454,6 +475,35 @@ void createGenesisChildren(AddressBook addressBook, long seqStart) {
setChild(StateChildIndices.SCHEDULE_TXS, new MerkleMap<>());
setChild(StateChildIndices.RECORD_STREAM_RUNNING_HASH, genesisRunningHashLeaf());
setChild(StateChildIndices.ADDRESS_BOOK, addressBook);

final ContractKeySerializer keySerializer = new ContractKeySerializer();
try {

final var leafRecordSerializer = new VirtualLeafRecordSerializer<>(
1,
DigestType.SHA_384,
1,
keySerializer.getSerializedSize(),
ContractKey::new,
1,
32,
ContractValue::new,
true);
final VirtualDataSource<ContractKey, ContractValue> ds =
new VirtualDataSourceJasperDB<>(
leafRecordSerializer,
new VirtualInternalRecordSerializer(),
keySerializer,
Path.of("data/jasperdb"),
50_000_000,
true,
0,
false
);
setChild(StateChildIndices.CONTRACT_STORAGE, new VirtualMap<>(ds));
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
}

private RecordsRunningHashLeaf genesisRunningHashLeaf() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package com.hedera.services.contracts.virtual;

/*-
* ‌
* Hedera Services Node
* ​
* Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC
* ​
* 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.
* ‍
*/

import com.swirlds.common.io.SerializableDataInputStream;
import com.swirlds.common.io.SerializableDataOutputStream;
import com.swirlds.virtualmap.VirtualKey;
import org.hyperledger.besu.datatypes.Address;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Objects;

public class ContractKey implements VirtualKey {
static final long RUNTIME_CONSTRUCTABLE_ID = 0xbd02f789da2d61a5L;
static final int RELEASE_0200_VERSION = 1;

private static final int KEY_SIZE = 32;
private static final int SIZE = Address.SIZE + KEY_SIZE;

private byte[] contract;

private byte[] key;

public ContractKey() {
this.contract = new byte[Address.SIZE];
this.key = new byte[KEY_SIZE];
}

public ContractKey(byte[] contract, byte[] key) {
this.setContract(contract);
this.setKey(key);
}

private void setContract(byte[] contract) {
Objects.requireNonNull(contract);
if (contract.length != Address.SIZE) {
throw new IllegalArgumentException("invalid contract address length");
}
this.contract = contract;
}

private void setKey(byte[] key) {
Objects.requireNonNull(key);
if (key.length != SIZE) {
throw new IllegalArgumentException("invalid key length");
}
this.key = key;
}

@Override
public void serialize(ByteBuffer byteBuffer) throws IOException {
byteBuffer.put(contract);
byteBuffer.put(key);
}

@Override
public void deserialize(ByteBuffer byteBuffer, int i) throws IOException {
byteBuffer.get(contract);
byteBuffer.get(key);
}

@Override
public boolean equals(ByteBuffer byteBuffer, int i) throws IOException {
byte[] contract = new byte[Address.SIZE];
byteBuffer.get(contract);
byte[] key = new byte[KEY_SIZE];
byteBuffer.get(key);

return Arrays.equals(this.contract, contract) && Arrays.equals(this.key, key);
}

@Override
public void deserialize(SerializableDataInputStream serializableDataInputStream, int i) throws IOException {
serializableDataInputStream.read(contract);
serializableDataInputStream.read(contract);
}

@Override
public long getClassId() {
return RUNTIME_CONSTRUCTABLE_ID;
}

@Override
public void serialize(SerializableDataOutputStream serializableDataOutputStream) throws IOException {
serializableDataOutputStream.write(contract);
serializableDataOutputStream.write(key);
}

@Override
public int getVersion() {
return RELEASE_0200_VERSION;
}

@Override
public int hashCode() {
return Objects.hash(Arrays.hashCode(this.contract), Arrays.hashCode(this.key));
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ContractKey that = (ContractKey) o;
return Arrays.equals(this.contract, that.contract) && Arrays.equals(this.key, that.key);
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("Uint256Key{");
sb.append("contract:(");
for (int i = 0; i < 20; i++) {
sb.append(String.format("%02X ", this.contract[i]).toUpperCase());
}
sb.append("),");
sb.append("key:(");
for (int i = 0; i < 32; i++) {
sb.append(String.format("%02X ", this.key[i]).toUpperCase());
}
sb.append(")}");

return sb.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.hedera.services.contracts.virtual;

/*-
* ‌
* Hedera Services Node
* ​
* Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC
* ​
* 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.
* ‍
*/

import com.swirlds.common.io.SerializableDataOutputStream;
import com.swirlds.jasperdb.files.hashmap.KeySerializer;

import java.io.IOException;
import java.nio.ByteBuffer;

public class ContractKeySerializer implements KeySerializer<ContractKey> {
@Override
public int deserializeKeySize(ByteBuffer byteBuffer) {
return 52; // TODO:
}

@Override
public int getSerializedSize() {
return 52; // TODO:
}

@Override
public long getCurrentDataVersion() {
return 1;
}

@Override
public ContractKey deserialize(ByteBuffer byteBuffer, long dataVersion) throws IOException {
final ContractKey key = new ContractKey();
key.deserialize(byteBuffer, (int) dataVersion);
return key;
}

@Override
public int serialize(ContractKey contractKey, SerializableDataOutputStream serializableDataOutputStream) throws IOException {
contractKey.serialize(serializableDataOutputStream);
return 52; // TODO:
}
}
Loading

0 comments on commit f614637

Please sign in to comment.