Skip to content

Commit 85da208

Browse files
authored
GetBlobSidecars API update (Consensys#8278)
* add class BlobSidecarsAndMetaData Signed-off-by: Gabriel Fukushima <[email protected]> * add spec and apply metadata logic to result Signed-off-by: Gabriel Fukushima <[email protected]> * fix tests Signed-off-by: Gabriel Fukushima <[email protected]> * add TODO Signed-off-by: Gabriel Fukushima <[email protected]> * Add changelog Signed-off-by: Gabriel Fukushima <[email protected]> * clean up and remove todo Signed-off-by: Gabriel Fukushima <[email protected]> * rename var Signed-off-by: Gabriel Fukushima <[email protected]> * add unit tests for metadata Signed-off-by: Gabriel Fukushima <[email protected]> --------- Signed-off-by: Gabriel Fukushima <[email protected]>
1 parent 3004a66 commit 85da208

File tree

11 files changed

+284
-44
lines changed

11 files changed

+284
-44
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ the [releases page](https://github.com/Consensys/teku/releases).
1212
### Breaking Changes
1313

1414
### Additions and Improvements
15+
- Added metadata fields to getBlobSidecars response.
1516

1617
### Bug Fixes

data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/schema/GetBlobSidecarsResponse.json

+10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@
33
"type" : "object",
44
"required" : [ "data" ],
55
"properties" : {
6+
"version" : {
7+
"type" : "string",
8+
"enum" : [ "phase0", "altair", "bellatrix", "capella", "deneb", "electra" ]
9+
},
10+
"execution_optimistic" : {
11+
"type" : "boolean"
12+
},
13+
"finalized" : {
14+
"type" : "boolean"
15+
},
616
"data" : {
717
"type" : "array",
818
"items" : {

data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetBlobSidecars.java

+17-8
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,18 @@
1515

1616
import static tech.pegasys.teku.beaconrestapi.BeaconRestApiTypes.BLOB_INDICES_PARAMETER;
1717
import static tech.pegasys.teku.beaconrestapi.BeaconRestApiTypes.PARAMETER_BLOCK_ID;
18+
import static tech.pegasys.teku.ethereum.json.types.EthereumTypes.MILESTONE_TYPE;
1819
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK;
20+
import static tech.pegasys.teku.infrastructure.http.RestApiConstants.EXECUTION_OPTIMISTIC;
21+
import static tech.pegasys.teku.infrastructure.http.RestApiConstants.FINALIZED;
1922
import static tech.pegasys.teku.infrastructure.http.RestApiConstants.TAG_BEACON;
23+
import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.BOOLEAN_TYPE;
2024
import static tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition.listOf;
2125

2226
import com.fasterxml.jackson.core.JsonProcessingException;
2327
import java.util.Collections;
2428
import java.util.List;
2529
import java.util.Optional;
26-
import java.util.function.Function;
2730
import tech.pegasys.teku.api.ChainDataProvider;
2831
import tech.pegasys.teku.api.DataProvider;
2932
import tech.pegasys.teku.infrastructure.async.SafeFuture;
@@ -38,6 +41,7 @@
3841
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
3942
import tech.pegasys.teku.spec.SpecMilestone;
4043
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;
44+
import tech.pegasys.teku.spec.datastructures.metadata.BlobSidecarsAndMetaData;
4145
import tech.pegasys.teku.spec.schemas.SchemaDefinitionCache;
4246
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb;
4347

@@ -75,7 +79,7 @@ private static EndpointMetadata createEndpointMetadata(final SchemaDefinitionCac
7579
@Override
7680
public void handleRequest(final RestApiRequest request) throws JsonProcessingException {
7781
final List<UInt64> indices = request.getQueryParameterList(BLOB_INDICES_PARAMETER);
78-
final SafeFuture<Optional<List<BlobSidecar>>> future =
82+
final SafeFuture<Optional<BlobSidecarsAndMetaData>> future =
7983
chainDataProvider.getBlobSidecars(request.getPathParameter(PARAMETER_BLOCK_ID), indices);
8084

8185
request.respondAsync(
@@ -86,21 +90,26 @@ public void handleRequest(final RestApiRequest request) throws JsonProcessingExc
8690
.orElse(AsyncApiResponse.respondNotFound())));
8791
}
8892

89-
private static SerializableTypeDefinition<List<BlobSidecar>> getResponseType(
93+
private static SerializableTypeDefinition<BlobSidecarsAndMetaData> getResponseType(
9094
final SchemaDefinitionCache schemaCache) {
9195
final DeserializableTypeDefinition<BlobSidecar> blobSidecarType =
9296
SchemaDefinitionsDeneb.required(schemaCache.getSchemaDefinition(SpecMilestone.DENEB))
9397
.getBlobSidecarSchema()
9498
.getJsonTypeDefinition();
95-
return SerializableTypeDefinition.<List<BlobSidecar>>object()
99+
return SerializableTypeDefinition.<BlobSidecarsAndMetaData>object()
96100
.name("GetBlobSidecarsResponse")
97-
.withField("data", listOf(blobSidecarType), Function.identity())
101+
.withOptionalField("version", MILESTONE_TYPE, (b) -> Optional.of(b.getMilestone()))
102+
.withOptionalField(
103+
EXECUTION_OPTIMISTIC, BOOLEAN_TYPE, (b) -> Optional.of(b.isExecutionOptimistic()))
104+
.withOptionalField(FINALIZED, BOOLEAN_TYPE, (b) -> Optional.of(b.isFinalized()))
105+
.withField("data", listOf(blobSidecarType), BlobSidecarsAndMetaData::getData)
98106
.build();
99107
}
100108

101-
private static ResponseContentTypeDefinition<List<BlobSidecar>> getSszResponseType() {
102-
final OctetStreamResponseContentTypeDefinition.OctetStreamSerializer<List<BlobSidecar>>
103-
serializer = (data, out) -> data.forEach(blobSidecar -> blobSidecar.sszSerialize(out));
109+
private static ResponseContentTypeDefinition<BlobSidecarsAndMetaData> getSszResponseType() {
110+
final OctetStreamResponseContentTypeDefinition.OctetStreamSerializer<BlobSidecarsAndMetaData>
111+
serializer =
112+
(data, out) -> data.getData().forEach(blobSidecar -> blobSidecar.sszSerialize(out));
104113

105114
return new OctetStreamResponseContentTypeDefinition<>(serializer, __ -> Collections.emptyMap());
106115
}

data/beaconrestapi/src/test/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetBlobSidecarsTest.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import tech.pegasys.teku.spec.SpecMilestone;
3535
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;
3636
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
37+
import tech.pegasys.teku.spec.datastructures.metadata.BlobSidecarsAndMetaData;
3738
import tech.pegasys.teku.spec.datastructures.metadata.ObjectAndMetaData;
3839

3940
class GetBlobSidecarsTest extends AbstractMigratedBeaconHandlerWithChainDataProviderTest {
@@ -61,7 +62,8 @@ void shouldReturnBlobSidecars()
6162
handler.handleRequest(request);
6263

6364
assertThat(request.getResponseCode()).isEqualTo(SC_OK);
64-
assertThat(request.getResponseBody()).isEqualTo(blobSidecars);
65+
assertThat(((BlobSidecarsAndMetaData) request.getResponseBody()).getData())
66+
.isEqualTo(blobSidecars);
6567
}
6668

6769
@Test
@@ -82,8 +84,9 @@ void metadata_shouldHandle500() throws JsonProcessingException {
8284
@Test
8385
void metadata_shouldHandle200() throws IOException {
8486
final List<BlobSidecar> blobSidecars = dataStructureUtil.randomBlobSidecars(3);
85-
86-
final String data = getResponseStringFromMetadata(handler, SC_OK, blobSidecars);
87+
final BlobSidecarsAndMetaData blobSidecarsAndMetaData =
88+
new BlobSidecarsAndMetaData(blobSidecars, SpecMilestone.DENEB, true, false, false);
89+
final String data = getResponseStringFromMetadata(handler, SC_OK, blobSidecarsAndMetaData);
8790
final String expected =
8891
Resources.toString(
8992
Resources.getResource(GetBlobSidecarsTest.class, "getBlobSidecars.json"), UTF_8);

data/beaconrestapi/src/test/resources/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/getBlobSidecars.json

+1-1
Large diffs are not rendered by default.

data/provider/src/main/java/tech/pegasys/teku/api/ChainDataProvider.java

+8-3
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
import tech.pegasys.teku.spec.datastructures.forkchoice.ProtoNodeData;
6666
import tech.pegasys.teku.spec.datastructures.forkchoice.ReadOnlyForkChoiceStrategy;
6767
import tech.pegasys.teku.spec.datastructures.lightclient.LightClientBootstrap;
68+
import tech.pegasys.teku.spec.datastructures.metadata.BlobSidecarsAndMetaData;
6869
import tech.pegasys.teku.spec.datastructures.metadata.BlockAndMetaData;
6970
import tech.pegasys.teku.spec.datastructures.metadata.ObjectAndMetaData;
7071
import tech.pegasys.teku.spec.datastructures.metadata.StateAndMetaData;
@@ -100,7 +101,7 @@ public ChainDataProvider(
100101
combinedChainDataClient,
101102
new BlockSelectorFactory(spec, combinedChainDataClient),
102103
new StateSelectorFactory(spec, combinedChainDataClient),
103-
new BlobSidecarSelectorFactory(combinedChainDataClient),
104+
new BlobSidecarSelectorFactory(spec, combinedChainDataClient),
104105
rewardCalculator);
105106
}
106107

@@ -179,7 +180,7 @@ public SafeFuture<Optional<ObjectAndMetaData<SignedBeaconBlock>>> getBlock(
179180
return fromBlock(blockIdParam, Function.identity());
180181
}
181182

182-
public SafeFuture<Optional<List<BlobSidecar>>> getBlobSidecars(
183+
public SafeFuture<Optional<BlobSidecarsAndMetaData>> getBlobSidecars(
183184
final String blockIdParam, final List<UInt64> indices) {
184185
return blobSidecarSelectorFactory
185186
.createSelectorForBlockId(blockIdParam)
@@ -188,7 +189,11 @@ public SafeFuture<Optional<List<BlobSidecar>>> getBlobSidecars(
188189

189190
public SafeFuture<Optional<List<BlobSidecar>>> getAllBlobSidecarsAtSlot(
190191
final UInt64 slot, final List<UInt64> indices) {
191-
return blobSidecarSelectorFactory.slotSelectorForAll(slot).getBlobSidecars(indices);
192+
return blobSidecarSelectorFactory
193+
.slotSelectorForAll(slot)
194+
.getBlobSidecars(indices)
195+
.thenApply(
196+
maybeBlobSideCarsMetaData -> maybeBlobSideCarsMetaData.map(ObjectAndMetaData::getData));
192197
}
193198

194199
public SafeFuture<Optional<ObjectAndMetaData<Bytes32>>> getBlockRoot(final String blockIdParam) {

data/provider/src/main/java/tech/pegasys/teku/api/blobselector/BlobSidecarSelector.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
import java.util.Optional;
1818
import tech.pegasys.teku.infrastructure.async.SafeFuture;
1919
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
20-
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;
20+
import tech.pegasys.teku.spec.datastructures.metadata.BlobSidecarsAndMetaData;
2121

2222
public interface BlobSidecarSelector {
23-
SafeFuture<Optional<List<BlobSidecar>>> getBlobSidecars(List<UInt64> indices);
23+
SafeFuture<Optional<BlobSidecarsAndMetaData>> getBlobSidecars(List<UInt64> indices);
2424
}

data/provider/src/main/java/tech/pegasys/teku/api/blobselector/BlobSidecarSelectorFactory.java

+125-9
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,22 @@
2222
import tech.pegasys.teku.api.AbstractSelectorFactory;
2323
import tech.pegasys.teku.infrastructure.async.SafeFuture;
2424
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
25+
import tech.pegasys.teku.spec.Spec;
2526
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;
2627
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
2728
import tech.pegasys.teku.spec.datastructures.blocks.SlotAndBlockRoot;
2829
import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.deneb.BeaconBlockBodyDeneb;
30+
import tech.pegasys.teku.spec.datastructures.metadata.BlobSidecarsAndMetaData;
31+
import tech.pegasys.teku.storage.client.ChainHead;
2932
import tech.pegasys.teku.storage.client.CombinedChainDataClient;
3033

3134
public class BlobSidecarSelectorFactory extends AbstractSelectorFactory<BlobSidecarSelector> {
3235

33-
public BlobSidecarSelectorFactory(final CombinedChainDataClient client) {
36+
private final Spec spec;
37+
38+
public BlobSidecarSelectorFactory(final Spec spec, final CombinedChainDataClient client) {
3439
super(client);
40+
this.spec = spec;
3541
}
3642

3743
@Override
@@ -44,11 +50,23 @@ public BlobSidecarSelector blockRootSelector(final Bytes32 blockRoot) {
4450
if (maybeSlot.isPresent()) {
4551
final SlotAndBlockRoot slotAndBlockRoot =
4652
new SlotAndBlockRoot(maybeSlot.get(), blockRoot);
47-
return getBlobSidecars(slotAndBlockRoot, indices);
53+
return getBlobSidecars(slotAndBlockRoot, indices)
54+
.thenApply(blobSidecars -> addMetaData(blobSidecars, slotAndBlockRoot));
4855
}
4956
return client
5057
.getBlockByBlockRoot(blockRoot)
51-
.thenCompose(maybeBlock -> getBlobSidecarsForBlock(maybeBlock, indices));
58+
.thenCompose(
59+
maybeBlock -> {
60+
if (maybeBlock.isEmpty()) {
61+
return SafeFuture.completedFuture(Optional.empty());
62+
}
63+
final SignedBeaconBlock block = maybeBlock.get();
64+
final SlotAndBlockRoot slotAndBlockRoot =
65+
new SlotAndBlockRoot(block.getSlot(), blockRoot);
66+
return getBlobSidecarsForBlock(maybeBlock, indices)
67+
.thenApply(
68+
blobSidecars -> addMetaData(blobSidecars, slotAndBlockRoot));
69+
});
5270
});
5371
}
5472

@@ -57,7 +75,13 @@ public BlobSidecarSelector headSelector() {
5775
return indices ->
5876
client
5977
.getChainHead()
60-
.map(head -> getBlobSidecars(head.getSlotAndBlockRoot(), indices))
78+
.map(
79+
head ->
80+
getBlobSidecars(head.getSlotAndBlockRoot(), indices)
81+
.thenApply(
82+
blobSideCars ->
83+
addMetaData(
84+
blobSideCars, head.getSlotAndBlockRoot(), head.isOptimistic())))
6185
.orElse(SafeFuture.completedFuture(Optional.empty()));
6286
}
6387

@@ -66,27 +90,58 @@ public BlobSidecarSelector genesisSelector() {
6690
return indices ->
6791
client
6892
.getBlockAtSlotExact(GENESIS_SLOT)
69-
.thenCompose(maybeGenesisBlock -> getBlobSidecarsForBlock(maybeGenesisBlock, indices));
93+
.thenCompose(
94+
maybeGenesisBlock ->
95+
getBlobSidecarsForBlock(maybeGenesisBlock, indices)
96+
.thenApply(
97+
blobSidecars ->
98+
addMetaData(
99+
blobSidecars,
100+
GENESIS_SLOT,
101+
false,
102+
true,
103+
client.isFinalized(GENESIS_SLOT))));
70104
}
71105

72106
@Override
73107
public BlobSidecarSelector finalizedSelector() {
74108
return indices ->
75109
client
76110
.getLatestFinalized()
77-
.map(anchorPoint -> getBlobSidecars(anchorPoint.getSlotAndBlockRoot(), indices))
111+
.map(
112+
anchorPoint ->
113+
getBlobSidecars(anchorPoint.getSlotAndBlockRoot(), indices)
114+
.thenApply(
115+
blobSideCars ->
116+
addMetaData(
117+
blobSideCars,
118+
anchorPoint.getSlotAndBlockRoot(),
119+
client.isChainHeadOptimistic())))
78120
.orElse(SafeFuture.completedFuture(Optional.empty()));
79121
}
80122

81123
@Override
82124
public BlobSidecarSelector slotSelector(final UInt64 slot) {
83125
return indices -> {
84126
if (client.isFinalized(slot)) {
85-
return getBlobSidecars(slot, indices);
127+
return getBlobSidecars(slot, indices)
128+
.thenApply(
129+
blobSidecars ->
130+
addMetaData(blobSidecars, slot, client.isChainHeadOptimistic(), true, true));
86131
}
87132
return client
88133
.getBlockAtSlotExact(slot)
89-
.thenCompose(maybeBlock -> getBlobSidecarsForBlock(maybeBlock, indices));
134+
.thenCompose(
135+
maybeBlock ->
136+
getBlobSidecarsForBlock(maybeBlock, indices)
137+
.thenApply(
138+
blobSidecars ->
139+
addMetaData(
140+
blobSidecars,
141+
slot,
142+
client.isChainHeadOptimistic(),
143+
false,
144+
client.isFinalized(slot))));
90145
};
91146
}
92147

@@ -96,7 +151,12 @@ public BlobSidecarSelector slotSelectorForAll(final UInt64 slot) {
96151
.getAllBlobSidecars(slot, indices)
97152
.thenApply(
98153
blobSidecars ->
99-
blobSidecars.isEmpty() ? Optional.empty() : Optional.of(blobSidecars));
154+
blobSidecars.isEmpty()
155+
? Optional.empty()
156+
: addMetaData(
157+
// We don't care about metadata since the api (teku only) that
158+
// consumes the return value doesn't use it
159+
Optional.of(blobSidecars), new SlotAndBlockRoot(slot, Bytes32.ZERO)));
100160
}
101161

102162
private SafeFuture<Optional<List<BlobSidecar>>> getBlobSidecarsForBlock(
@@ -125,4 +185,60 @@ private SafeFuture<Optional<List<BlobSidecar>>> getBlobSidecars(
125185
final UInt64 slot, final List<UInt64> indices) {
126186
return client.getBlobSidecars(slot, indices).thenApply(Optional::of);
127187
}
188+
189+
private Optional<BlobSidecarsAndMetaData> addMetaData(
190+
final Optional<List<BlobSidecar>> maybeBlobSidecarList,
191+
final SlotAndBlockRoot slotAndBlockRoot) {
192+
if (maybeBlobSidecarList.isEmpty()) {
193+
return Optional.empty();
194+
}
195+
196+
final UInt64 slot = slotAndBlockRoot.getSlot();
197+
final Bytes32 blockRoot = slotAndBlockRoot.getBlockRoot();
198+
final Optional<ChainHead> maybeChainHead = client.getChainHead();
199+
final boolean isFinalized = client.isFinalized(slot);
200+
boolean isOptimistic;
201+
boolean isCanonical = false;
202+
203+
if (maybeChainHead.isPresent()) {
204+
ChainHead chainHead = maybeChainHead.get();
205+
isOptimistic = chainHead.isOptimistic() || client.isOptimisticBlock(blockRoot);
206+
isCanonical = client.isCanonicalBlock(slot, blockRoot, chainHead.getRoot());
207+
} else {
208+
// If there's no chain head, we assume the block is not optimistic and not canonical
209+
isOptimistic = client.isOptimisticBlock(blockRoot);
210+
}
211+
return addMetaData(maybeBlobSidecarList, slot, isOptimistic, isCanonical, isFinalized);
212+
}
213+
214+
private Optional<BlobSidecarsAndMetaData> addMetaData(
215+
final Optional<List<BlobSidecar>> maybeBlobSidecarList,
216+
final SlotAndBlockRoot slotAndBlockRoot,
217+
final boolean isOptimistic) {
218+
if (maybeBlobSidecarList.isEmpty()) {
219+
return Optional.empty();
220+
}
221+
return addMetaData(
222+
maybeBlobSidecarList,
223+
slotAndBlockRoot.getSlot(),
224+
isOptimistic,
225+
true,
226+
client.isFinalized(slotAndBlockRoot.getSlot()));
227+
}
228+
229+
private Optional<BlobSidecarsAndMetaData> addMetaData(
230+
final Optional<List<BlobSidecar>> maybeBlobSidecarList,
231+
final UInt64 blockSlot,
232+
final boolean executionOptimistic,
233+
final boolean canonical,
234+
final boolean finalized) {
235+
return maybeBlobSidecarList.map(
236+
blobSidecarList ->
237+
new BlobSidecarsAndMetaData(
238+
blobSidecarList,
239+
spec.atSlot(blockSlot).getMilestone(),
240+
executionOptimistic,
241+
canonical,
242+
finalized));
243+
}
128244
}

data/provider/src/test/java/tech/pegasys/teku/api/AbstractChainDataProviderTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ protected ChainDataProvider setupBySpec(
104104
this.blockSelectorFactory = spy(new BlockSelectorFactory(spec, mockCombinedChainDataClient));
105105
this.stateSelectorFactory = spy(new StateSelectorFactory(spec, mockCombinedChainDataClient));
106106
this.blobSidecarSelectorFactory =
107-
spy(new BlobSidecarSelectorFactory(mockCombinedChainDataClient));
107+
spy(new BlobSidecarSelectorFactory(spec, mockCombinedChainDataClient));
108108
final ChainDataProvider provider =
109109
new ChainDataProvider(
110110
spec,

0 commit comments

Comments
 (0)