Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stable Containers - EIP-7495 #8444

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions infrastructure/ssz/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ dependencies {
implementation 'org.apache.tuweni:tuweni-units'

testImplementation testFixtures(project(':infrastructure:collections'))
testImplementation testFixtures(project(':infrastructure:json'))
testImplementation testFixtures(project(':infrastructure:serviceutils'))

testFixturesApi 'org.apache.tuweni:tuweni-bytes'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

package tech.pegasys.teku.infrastructure.ssz;

import java.util.NoSuchElementException;
import tech.pegasys.teku.infrastructure.ssz.schema.SszCompositeSchema;

/**
Expand All @@ -30,7 +31,10 @@ default int size() {
/**
* Returns the child at index
*
* @throws IndexOutOfBoundsException if index >= size()
* @throws IndexOutOfBoundsException if index > last valid index (which is size() - 1 for
* non-sparse containers like StableContainer)
* @throws NoSuchElementException if index <= last valid index but field was not found (for sparse
* containers like StableContainer)
*/
SszChildT get(int index);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright Consensys Software Inc., 2024
*
* 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.
*/

package tech.pegasys.teku.infrastructure.ssz;

public interface SszProfile extends SszStableContainerBase {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright Consensys Software Inc., 2024
*
* 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.
*/

package tech.pegasys.teku.infrastructure.ssz;

public interface SszStableContainer extends SszStableContainerBase {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright Consensys Software Inc., 2024
*
* 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.
*/

package tech.pegasys.teku.infrastructure.ssz;

import java.util.NoSuchElementException;
import java.util.Optional;
import tech.pegasys.teku.infrastructure.ssz.collections.SszBitvector;

public interface SszStableContainerBase extends SszContainer {
boolean isFieldActive(int index);

SszBitvector getActiveFields();

default Optional<SszData> getOptional(final int index) {
try {
return Optional.of(get(index));
} catch (final NoSuchElementException __) {
return Optional.empty();
}
}

@SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
// container is heterogeneous by its nature so making unsafe cast here
// is more convenient and is not less safe
default <C extends SszData> Optional<C> getAnyOptional(final int index) {
return (Optional<C>) getOptional(index);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package tech.pegasys.teku.infrastructure.ssz.impl;

import com.google.common.base.Suppliers;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.function.Supplier;
import tech.pegasys.teku.infrastructure.ssz.SszComposite;
Expand Down Expand Up @@ -132,6 +133,8 @@ public final int size() {
* Checks the child index
*
* @throws IndexOutOfBoundsException if index is invalid
* @throws NoSuchElementException if field is not present (for sparse data structures like
* StableContainers)
*/
protected abstract void checkIndex(int index);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import tech.pegasys.teku.infrastructure.ssz.SszMutableContainer;
import tech.pegasys.teku.infrastructure.ssz.cache.ArrayIntCache;
import tech.pegasys.teku.infrastructure.ssz.cache.IntCache;
import tech.pegasys.teku.infrastructure.ssz.schema.SszCompositeSchema;
import tech.pegasys.teku.infrastructure.ssz.schema.SszContainerSchema;
import tech.pegasys.teku.infrastructure.ssz.schema.impl.AbstractSszContainerSchema;
import tech.pegasys.teku.infrastructure.ssz.tree.TreeNode;
Expand All @@ -40,6 +41,11 @@ protected AbstractSszImmutableContainer(
super(schema, backingNode);
}

public AbstractSszImmutableContainer(
final SszCompositeSchema<?> type, final TreeNode backingNode, final IntCache<SszData> cache) {
super(type, backingNode, cache);
}

protected AbstractSszImmutableContainer(
final SszContainerSchema<? extends AbstractSszImmutableContainer> schema,
final SszData... memberValues) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public void set(final int index, final SszChildT value) {

childrenChanges.put(index, createChangeRecordByValue(immutableValue));

sizeCache = index >= sizeCache ? index + 1 : sizeCache;
sizeCache = calcNewSize(index);
invalidate();
}

Expand Down Expand Up @@ -239,6 +239,15 @@ public SszMutableComposite<SszChildT> createWritableCopy() {
*/
protected abstract void checkIndex(int index, boolean set);

/**
* Determines the new size given the field has been set or updated
*
* @param index of the field set or updated
*/
protected int calcNewSize(final int index) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should probably just call this calculateNewSize rather than abbreviate...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

problem is that in the ssz module seems like "calc" is the normal:
image

I wanted to maintain the style here. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok i guess but i do hate abbreviations...

return index >= sizeCache ? index + 1 : sizeCache;
}

private static final class ChildChangeRecord<
SszChildT extends SszData, SszMutableChildT extends SszChildT> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import tech.pegasys.teku.infrastructure.ssz.cache.IntCache;
import tech.pegasys.teku.infrastructure.ssz.schema.SszCompositeSchema;
import tech.pegasys.teku.infrastructure.ssz.schema.SszContainerSchema;
import tech.pegasys.teku.infrastructure.ssz.schema.impl.AbstractSszContainerSchema;
import tech.pegasys.teku.infrastructure.ssz.tree.TreeNode;

public class SszContainerImpl extends AbstractSszComposite<SszData> implements SszContainer {
Expand All @@ -48,8 +47,8 @@ protected SszData getImpl(final int index) {
}

@Override
public AbstractSszContainerSchema<?> getSchema() {
return (AbstractSszContainerSchema<?>) super.getSchema();
public SszContainerSchema<?> getSchema() {
return (SszContainerSchema<?>) super.getSchema();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import tech.pegasys.teku.infrastructure.ssz.SszMutableData;
import tech.pegasys.teku.infrastructure.ssz.SszMutableRefContainer;
import tech.pegasys.teku.infrastructure.ssz.cache.IntCache;
import tech.pegasys.teku.infrastructure.ssz.schema.impl.AbstractSszContainerSchema;
import tech.pegasys.teku.infrastructure.ssz.schema.SszContainerSchema;
import tech.pegasys.teku.infrastructure.ssz.tree.TreeNode;

public class SszMutableContainerImpl extends AbstractSszMutableComposite<SszData, SszMutableData>
Expand All @@ -36,8 +36,8 @@ protected SszContainerImpl createImmutableSszComposite(
}

@Override
public AbstractSszContainerSchema<?> getSchema() {
return (AbstractSszContainerSchema<?>) super.getSchema();
public SszContainerSchema<?> getSchema() {
return (SszContainerSchema<?>) super.getSchema();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,9 @@ public SszList<SszElementT> commitChanges() {
public SszMutableList<SszElementT> createWritableCopy() {
throw new UnsupportedOperationException("Creating a copy from writable list is not supported");
}

@Override
public String toString() {
return "Mutable" + backingImmutableData;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/

package tech.pegasys.teku.infrastructure.ssz.impl;

import java.util.NoSuchElementException;
import tech.pegasys.teku.infrastructure.ssz.SszData;
import tech.pegasys.teku.infrastructure.ssz.cache.IntCache;
import tech.pegasys.teku.infrastructure.ssz.tree.TreeNode;

public class SszMutableStableContainerBaseImpl extends SszMutableContainerImpl {
protected SszStableContainerBaseImpl backingStableContainerBaseView;

public SszMutableStableContainerBaseImpl(final SszStableContainerBaseImpl backingImmutableView) {
super(backingImmutableView);
this.backingStableContainerBaseView = backingImmutableView;
}

@Override
protected SszContainerImpl createImmutableSszComposite(
final TreeNode backingNode, final IntCache<SszData> viewCache) {
return new SszStableContainerBaseImpl(
getSchema().toStableContainerSchemaBaseRequired(), backingNode, viewCache);
}

@Override
protected void checkIndex(final int index, final boolean set) {
// we currently not support StableContainers with optional fields, so we expect get\set over an
// already active field
if (backingStableContainerBaseView.isFieldActive(index)) {
return;
}

if (index > backingStableContainerBaseView.getActiveFields().getLastSetBitIndex()) {
throw new IndexOutOfBoundsException(
"Invalid index "
+ index
+ " for container with last active index "
+ backingStableContainerBaseView.getActiveFields().getLastSetBitIndex());
}

throw new NoSuchElementException("Index " + index + " is not active in the stable container");
}

@Override
protected int calcNewSize(final int index) {
// StableContainer have sparse index so size cannot be compared with index.
// Currently, the only mutable StableContainer we support is a Profile with no optional
// fields, so we can assume the size never changes.
// See:
// tech.pegasys.teku.infrastructure.ssz.impl.SszStableContainerBaseImpl.createWritableCopy
return size();
}

@Override
public String toString() {
return "Mutable " + backingStableContainerBaseView.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright Consensys Software Inc., 2024
*
* 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.
*/

package tech.pegasys.teku.infrastructure.ssz.impl;

import com.google.common.base.Preconditions;
import java.util.Arrays;
import tech.pegasys.teku.infrastructure.ssz.SszData;
import tech.pegasys.teku.infrastructure.ssz.SszProfile;
import tech.pegasys.teku.infrastructure.ssz.cache.ArrayIntCache;
import tech.pegasys.teku.infrastructure.ssz.cache.IntCache;
import tech.pegasys.teku.infrastructure.ssz.schema.SszProfileSchema;
import tech.pegasys.teku.infrastructure.ssz.tree.TreeNode;

public class SszProfileImpl extends SszStableContainerBaseImpl implements SszProfile {

public SszProfileImpl(final SszProfileSchema<?> type) {
super(type);
}

public SszProfileImpl(final SszProfileSchema<?> type, final TreeNode backingNode) {
super(type, backingNode);
}

public SszProfileImpl(
final SszProfileSchema<?> type, final TreeNode backingNode, final IntCache<SszData> cache) {
super(type, backingNode, cache);
}

public SszProfileImpl(final SszProfileSchema<?> type, final SszData... memberValues) {
super(
type,
type.createTreeFromFieldValues(Arrays.asList(memberValues)),
createCache(memberValues));

for (int i = 0; i < memberValues.length; i++) {
Preconditions.checkArgument(
memberValues[i].getSchema().equals(type.getChildSchema(i)),
"Wrong child schema at index %s. Expected: %s, was %s",
i,
type.getChildSchema(i),
memberValues[i].getSchema());
}
}

private static IntCache<SszData> createCache(final SszData... memberValues) {
ArrayIntCache<SszData> cache = new ArrayIntCache<>(memberValues.length);
tbenr marked this conversation as resolved.
Show resolved Hide resolved
for (int i = 0; i < memberValues.length; i++) {
cache.invalidateWithNewValue(i, memberValues[i]);
}
return cache;
}
}
Loading